diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 50c276c8a0..fd38b3dc61 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ "hostRequirements": { "cpus": 4 }, - "onCreateCommand": "wget https://download.visualstudio.microsoft.com/download/pr/db901b0a-3144-4d07-b8ab-6e7a43e7a791/4d9d1b39b879ad969c6c0ceb6d052381/dotnet-sdk-8.0.401-linux-x64.tar.gz -O $HOME/dotnet.tar.gz && export DOTNET_ROOT=$HOME/.dotnet && mkdir -p \"$DOTNET_ROOT\" && tar zxf $HOME/dotnet.tar.gz -C \"$DOTNET_ROOT\" && export PATH=$DOTNET_ROOT:$DOTNET_ROOT/tools:$PATH && dotnet dev-certs https --trust && find . -type f -name '*.csproj' -exec sed -i 's/Microsoft.NET.Sdk.BlazorWebAssembly/Microsoft.NET.Sdk.Web/g' {} \\;", + "onCreateCommand": "wget https://download.visualstudio.microsoft.com/download/pr/1ebffeb0-f090-4001-9f13-69f112936a70/5dbc249b375cca13ec4d97d48ea93b28/dotnet-sdk-8.0.402-linux-x64.tar.gz -O $HOME/dotnet.tar.gz && export DOTNET_ROOT=$HOME/.dotnet && mkdir -p \"$DOTNET_ROOT\" && tar zxf $HOME/dotnet.tar.gz -C \"$DOTNET_ROOT\" && export PATH=$DOTNET_ROOT:$DOTNET_ROOT/tools:$PATH && dotnet dev-certs https --trust && find . -type f -name '*.csproj' -exec sed -i 's/Microsoft.NET.Sdk.BlazorWebAssembly/Microsoft.NET.Sdk.Web/g' {} \\;", "waitFor": "onCreateCommand", "customizations": { "codespaces": { diff --git a/.github/workflows/admin-sample.cd.yml b/.github/workflows/admin-sample.cd.yml index 5a1aa55e40..62ec9234e3 100644 --- a/.github/workflows/admin-sample.cd.yml +++ b/.github/workflows/admin-sample.cd.yml @@ -19,7 +19,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -36,26 +36,25 @@ jobs: cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage --api Standalone + cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage --api Standalone --notification --captcha reCaptcha --signalR - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Shared/appsettings.json' + files: 'AdminPanel/src/Shared/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Web/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Web/appsettings.Production.json' env: ServerAddress: ${{ env.API_SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + WebAppRender.BlazorMode: BlazorWebAssembly ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} + AdsPushVapid.PublicKey: ${{ secrets.ADMINPANEL_PUBLIC_VAPIDKEY }} - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Install wasm run: cd src && dotnet workload install wasm-tools - - - name: Configure app render mode - run: | - sed -i 's/BlazorAuto;/BlazorWebAssembly;/g' AdminPanel/src/Client/AdminPanel.Client.Core/Services/AppRenderMode.cs - name: Generate CSS/JS files run: dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release @@ -72,18 +71,19 @@ jobs: - name: Publish adminpanel blazor wasm standalone run: | sed -i 's/adminpanel.bitplatform.dev/adminpanel-api.bitplatform.dev/g' AdminPanel/src/Client/AdminPanel.Client.Web/wwwroot/index.html - dotnet publish AdminPanel/src/Client/AdminPanel.Client.Web/AdminPanel.Client.Web.csproj -c Release -p:BlazorWebAssemblyStandalone=true -o ${{env.DOTNET_ROOT}}/static + dotnet publish AdminPanel/src/Client/AdminPanel.Client.Web/AdminPanel.Client.Web.csproj -c Release -p:PwaEnabled=true -o ${{env.DOTNET_ROOT}}/static -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" - name: Upload static artifact uses: actions/upload-artifact@v4 with: name: static-bundle path: ${{env.DOTNET_ROOT}}/static + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} @@ -137,22 +137,23 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Create project from Boilerplate run: | cd src\Templates\Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ..\..\..\ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --windows --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ..\..\..\ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --windows --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage --captcha reCaptcha --signalR - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'AdminPanel\src\Client\AdminPanel.Client.Core\appsettings.json, AdminPanel\src\Shared\appsettings.json' + files: 'AdminPanel\src\Shared\appsettings.json, AdminPanel\src\Client\AdminPanel.Client.Core\appsettings.json, AdminPanel\src\Client\AdminPanel.Client.Windows\appsettings.json' env: ServerAddress: ${{ env.API_SERVER_ADDRESS }} - WindowsUpdateSettings.FilesUrl: https://windows-adminpanel.bitplatform.dev + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + WindowsUpdate.FilesUrl: https://windows-adminpanel.bitplatform.dev ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Generate CSS/JS files @@ -188,7 +189,7 @@ jobs: build_blazor_hybrid_android: name: build blazor hybrid (android) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -205,11 +206,11 @@ jobs: cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Extract Android signing key from env uses: timheuer/base64-to-file@v1.2 @@ -217,13 +218,21 @@ jobs: fileDir: './AdminPanel/src/Client/AdminPanel.Client.Maui/' fileName: 'AdminPanel.keystore' encodedString: ${{ secrets.ANDROID_RELEASE_KEYSTORE_FILE_BASE64 }} + + - name: Extract Android signing key from env + uses: timheuer/base64-to-file@v1.2 + with: + fileDir: './AdminPanel/src/Client/AdminPanel.Client.Maui/Platforms/Android' + fileName: 'google-services.json' + encodedString: ${{ secrets.ADMINPANEL_GOOGLE_SERVICES_JSON_BASE64 }} - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Shared/appsettings.json' + files: 'AdminPanel/src/Shared/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.API_SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Set app center secret @@ -252,7 +261,7 @@ jobs: build_blazor_hybrid_ios: name: build blazor hybrid (iOS-macOS) - runs-on: macos-14 + runs-on: macOS-15 steps: @@ -266,25 +275,26 @@ jobs: - uses: maxim-lobanov/setup-xcode@v1.6.0 with: - xcode-version: '15.4' + xcode-version: '16.0' - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Create project from Boilerplate run: | cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --sample Admin --appInsights --appCenter --serverUrl ${{ env.WEB_SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Shared/appsettings.json' + files: 'AdminPanel/src/Shared/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Core/appsettings.json, AdminPanel/src/Client/AdminPanel.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.API_SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Set app center secret diff --git a/.github/workflows/bit.ci.yml b/.github/workflows/bit.ci.yml index cf1a4dc49a..28079eb035 100644 --- a/.github/workflows/bit.ci.yml +++ b/.github/workflows/bit.ci.yml @@ -8,7 +8,7 @@ jobs: build-release: if: startsWith(github.event.pull_request.title, 'Prerelease') || startsWith(github.event.pull_request.title, 'Release') || startsWith(github.event.pull_request.title, 'Version') name: build and test for release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -19,17 +19,24 @@ jobs: uses: actions/setup-dotnet@v4 with: global-json-file: src/global.json + + - name: Setup .NET 6,7 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 6.0.x + 7.0.x - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Install wasm and maui run: cd src && dotnet workload install maui-android wasm-tools - name: Run BeforeBuildTasks continue-on-error: true # Error MSB4057, not all csproj files have BeforeBuildTasks target. - run: dotnet build src/Bit-CI-release.sln -t:BeforeBuildTasks -m:1 + run: dotnet build src/Bit-CI-release.sln -t:BeforeBuildTasks -m:1 -f net8.0 - name: MSBuild prerelease run: dotnet build src/Bit-CI-release.sln @@ -40,7 +47,7 @@ jobs: build: if: startsWith(github.event.pull_request.title, 'Prerelease') != true && startsWith(github.event.pull_request.title, 'Release') != true && startsWith(github.event.pull_request.title, 'Version') != true name: build and test - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -51,10 +58,17 @@ jobs: uses: actions/setup-dotnet@v4 with: global-json-file: src/global.json + + - name: Setup .NET 6,7 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 6.0.x + 7.0.x - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Install wasm and maui run: cd src && dotnet workload install maui-android wasm-tools @@ -64,7 +78,7 @@ jobs: - name: Run BeforeBuildTasks continue-on-error: true # Error MSB4057, not all csproj files have BeforeBuildTasks target. - run: dotnet build src/Bit-CI.sln -t:BeforeBuildTasks -m:1 + run: dotnet build src/Bit-CI.sln -t:BeforeBuildTasks -m:1 -f net8.0 - name: Build run: dotnet build src/Bit-CI.sln -p:WarningLevel=0 -p:RunCodeAnalysis=false diff --git a/.github/workflows/bit.full.ci.yml b/.github/workflows/bit.full.ci.yml index 29ef43b853..4535a3c30a 100644 --- a/.github/workflows/bit.full.ci.yml +++ b/.github/workflows/bit.full.ci.yml @@ -5,12 +5,16 @@ on: env: ConnectionStrings__SqlServerConnectionString: 'Data Source=localhost; Initial Catalog=BoilerplateDb;Application Name=Boilerplate;TrustServerCertificate=True;User Id=sa;Password=P@ssw0rdP@ssw0rd;' + SIMPLE_TEST_FILTER: "ClassName!~PageTests" + BLAZOR_SERVER_TEST_FILTER: "ClassName~PageTests.BlazorServer" + BLAZOR_WASM_TEST_FILTER: "ClassName~PageTests.BlazorWebAssembly" + MULTILINGUAL_DISABLED_TEST_FILTER: "ClassName!~LocalizationTests|TestCategory=MultilingualDisabled" jobs: build: name: build and test - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -22,13 +26,27 @@ jobs: with: global-json-file: src/global.json + - name: Setup .NET 6,7 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 6.0.x + 7.0.x + - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 + + - name: Uninstall Bit.Boilerplate if running on ACT + continue-on-error: true + if: ${{ env.ACT }} + run: dotnet new uninstall Bit.Boilerplate - name: Prepare environment run: | - cd src/Templates/Boilerplate && dotnet build -c Release + cd src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/ + sed -i 's|"ServerAddress": "http://localhost:5030/",|"ServerAddress": "/",|' appsettings.json + cd ../../../../ && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg && cd ../../../ dotnet workload install maui-tizen maui-android wasm-tools @@ -41,25 +59,87 @@ jobs: sa-password: P@ssw0rdP@ssw0rd show-log: true + - name: Simple tests (no --advancedTests) + id: simple-test + run: | + dotnet new bit-bp --name SimpleTest --database Sqlite + cd SimpleTest/src/Server/SimpleTest.Server.Api/ + dotnet tool restore + dotnet ef migrations add InitialMigration + dotnet ef database update + cd ../../Tests + dotnet build + pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps + dotnet test --logger GitHubActions --filter "${{ env.SIMPLE_TEST_FILTER }}" + + - name: Upload Tests Artifact + uses: actions/upload-artifact@v4.4.1 + if: ${{ !env.ACT && failure() && steps.simple-test.conclusion == 'failure' }} + with: + name: tests-artifact + path: ./SimpleTest/src/Tests/TestResults + retention-days: 14 + - name: Test Sqlite database option + id: sqlite-test run: | - dotnet new bit-bp --name TestSqlite --database Sqlite + dotnet new bit-bp --name TestSqlite --database Sqlite --advancedTests cd TestSqlite/src/Server/TestSqlite.Server.Api/ dotnet tool restore dotnet ef migrations add InitialMigration dotnet ef database update cd ../../Tests - dotnet test + dotnet build + pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps + dotnet test --logger GitHubActions --filter "${{ env.BLAZOR_SERVER_TEST_FILTER }}" + dotnet test --logger GitHubActions --filter "${{ env.BLAZOR_WASM_TEST_FILTER }}" -- MSTest.Parallelize.Workers=1 + + - name: Upload Tests Artifact + uses: actions/upload-artifact@v4.4.1 + if: ${{ !env.ACT && failure() && steps.sqlite-test.conclusion == 'failure' }} + with: + name: tests-artifact + path: ./TestSqlite/src/Tests/TestResults + retention-days: 14 - name: Test SqlServer database option + id: sqlserver-test run: | - dotnet new bit-bp --name TestSqlServer --database SqlServer + dotnet new bit-bp --name TestSqlServer --database SqlServer --advancedTests cd TestSqlServer/src/Server/TestSqlServer.Server.Api/ dotnet tool restore dotnet ef migrations add InitialMigration - dotnet ef database update + dotnet ef database update cd ../../Tests - dotnet test + dotnet test --logger GitHubActions --filter "${{ env.BLAZOR_SERVER_TEST_FILTER }}" + dotnet test --logger GitHubActions --filter "${{ env.BLAZOR_WASM_TEST_FILTER }}" -- MSTest.Parallelize.Workers=1 + + - name: Upload Tests Artifact + uses: actions/upload-artifact@v4.4.1 + if: ${{ !env.ACT && failure() && steps.sqlserver-test.conclusion == 'failure' }} + with: + name: tests-artifact + path: ./TestSqlServer/src/Tests/TestResults + retention-days: 14 + + - name: Test Multilingual disabled option + id: multilingual-disabled-test + run: | + dotnet new bit-bp --name MultilingualDisabled --database Sqlite --advancedTests + cd MultilingualDisabled/src/Server/MultilingualDisabled.Server.Api/ + dotnet tool restore + dotnet ef migrations add InitialMigration + dotnet ef database update + cd ../../Tests + dotnet test -p:MultilingualEnabled=false --logger GitHubActions --filter "${{ env.MULTILINGUAL_DISABLED_TEST_FILTER }}" -- MSTest.Parallelize.Workers=1 + + - name: Upload Tests Artifact + uses: actions/upload-artifact@v4.4.1 + if: ${{ !env.ACT && failure() && steps.multilingual-disabled-test.conclusion == 'failure' }} + with: + name: tests-artifact + path: ./MultilingualDisabled/src/Tests/TestResults + retention-days: 14 - name: Test PostgreSQL, Cosmos, MySql, Other database options run: | @@ -104,17 +184,17 @@ jobs: - name: Test sample configuration 1 run: | - dotnet new bit-bp --name TestProject --database Cosmos --filesStorage AzureBlobStorage --api Integrated --captcha reCaptcha --pipeline Azure --sample Admin --offlineDb true --windows true --appInsights true --appCenter true --signalr true - dotnet build TestProject/TestProject.sln -p:MultilingualEnabled=true -p:PWA=true -p:BlazorWebAssemblyStandalone=true -p:Environment=Staging + dotnet new bit-bp --name TestProject --database Cosmos --filesStorage AzureBlobStorage --api Integrated --captcha reCaptcha --pipeline Azure --sample Admin --offlineDb --windows --appInsights --appCenter --signalR --notification + dotnet build TestProject/TestProject.sln -p:MultilingualEnabled=true -p:PwaEnabled=true -p:Environment=Staging - name: Test sample configuration 2 run: | - dotnet new bit-bp --name TestProject2 --database Other --filesStorage Other --api Standalone --captcha None --pipeline None --sample None --offlineDb false --windows false --appInsights false --appCenter false --signalr false - dotnet build TestProject2/TestProject2.sln -p:MultilingualEnabled=false -p:PWA=false -p:BlazorWebAssemblyStandalone=false -p:Environment=Development + dotnet new bit-bp --name TestProject2 --database Other --filesStorage Other --api Standalone --captcha None --pipeline None --sample None --offlineDb false --windows false --appInsights false --appCenter false --signalR false --notification false + dotnet build TestProject2/TestProject2.sln -p:MultilingualEnabled=false -p:PwaEnabled=false -p:Environment=Development - name: Run BeforeBuildTasks continue-on-error: true # Error MSB4057, not all csproj files have BeforeBuildTasks target. - run: dotnet build src/Bit-CI-release.sln -t:BeforeBuildTasks -m:1 + run: dotnet build src/Bit-CI-release.sln -t:BeforeBuildTasks -m:1 -f net8.0 - name: Release build bit blazor ui + butil + bswup + besql + bup + code analyzers + source generators run: dotnet build src/Bit-CI-release.sln -c Release diff --git a/.github/workflows/blazorui.demo.cd.yml b/.github/workflows/blazorui.demo.cd.yml index 6b5090ac1e..1a24cbab6a 100644 --- a/.github/workflows/blazorui.demo.cd.yml +++ b/.github/workflows/blazorui.demo.cd.yml @@ -16,7 +16,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -37,7 +37,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Install wasm run: cd src && dotnet workload install wasm-tools @@ -56,11 +56,12 @@ jobs: with: name: server-bundle path: ${{env.DOTNET_ROOT}}/server + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} @@ -103,7 +104,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Update appsettings.json api server address uses: devops-actions/variable-substitution@v1.2 @@ -142,7 +143,7 @@ jobs: build_blazor_hybrid_android: name: build blazor hybrid (android) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -156,7 +157,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Extract Android signing key from env uses: timheuer/base64-to-file@v1.2 @@ -192,7 +193,7 @@ jobs: build_blazor_hybrid_ios: name: build blazor hybrid (iOS-macOS) - runs-on: macos-14 + runs-on: macOS-15 steps: @@ -206,11 +207,11 @@ jobs: - uses: maxim-lobanov/setup-xcode@v1.6.0 with: - xcode-version: '15.4' + xcode-version: '16.0' - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Update appsettings.json api server address uses: devops-actions/variable-substitution@v1.2 diff --git a/.github/workflows/nuget.org.yml b/.github/workflows/nuget.org.yml index 764b3baf36..369331d462 100644 --- a/.github/workflows/nuget.org.yml +++ b/.github/workflows/nuget.org.yml @@ -17,7 +17,7 @@ on: jobs: build-publish: name: build & publish - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -31,7 +31,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Delete AssemblyOriginatorKeyFile.snk run: | diff --git a/.github/workflows/platform.website.cd.yml b/.github/workflows/platform.website.cd.yml index 980e65ab9d..fda20fbb73 100644 --- a/.github/workflows/platform.website.cd.yml +++ b/.github/workflows/platform.website.cd.yml @@ -15,7 +15,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -41,11 +41,12 @@ jobs: with: name: server-bundle path: server + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} diff --git a/.github/workflows/prerelease.nuget.org.yml b/.github/workflows/prerelease.nuget.org.yml index b40cd961f2..cd465fc4ee 100644 --- a/.github/workflows/prerelease.nuget.org.yml +++ b/.github/workflows/prerelease.nuget.org.yml @@ -9,7 +9,7 @@ on: jobs: build-publish: name: build & publish - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout source code uses: actions/checkout@v4 @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Delete AssemblyOriginatorKeyFile.snk run: | diff --git a/.github/workflows/sales.website.cd.yml b/.github/workflows/sales.website.cd.yml index 70fbecbba2..4208d60579 100644 --- a/.github/workflows/sales.website.cd.yml +++ b/.github/workflows/sales.website.cd.yml @@ -15,7 +15,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -41,11 +41,12 @@ jobs: with: name: server-bundle path: server + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} diff --git a/.github/workflows/todo-sample.cd.yml b/.github/workflows/todo-sample.cd.yml index 84d1448da8..83f2d4a09f 100644 --- a/.github/workflows/todo-sample.cd.yml +++ b/.github/workflows/todo-sample.cd.yml @@ -17,7 +17,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -31,34 +31,30 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Create project from Boilerplate run: | cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Shared/appsettings.json' + files: 'TodoSample/src/Shared/appsettings.json, TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Client/TodoSample.Client.Web/appsettings.json, TodoSample/src/Client/TodoSample.Client.Web/appsettings.Production.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + WebAppRender.BlazorMode: BlazorWebAssembly + WebAppRender.PrerenderEnabled: true ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} + AdsPushVapid.PublicKey: ${{ secrets.TODO_PUBLIC_VAPIDKEY }} - name: Install wasm run: cd src && dotnet workload install wasm-tools - - name: Configure app render mode - run: | - sed -i 's/public static readonly bool PrerenderEnabled = false;/public static readonly bool PrerenderEnabled = true;/g' TodoSample/src/Client/TodoSample.Client.Core/Services/AppRenderMode.cs - sed -i 's/BlazorAuto;/BlazorWebAssembly;/g' TodoSample/src/Client/TodoSample.Client.Core/Services/AppRenderMode.cs - - - name: Changes for static-todo.bitplatform.dev - Part 1 - run: sed -i 's/http:\/\/localhost:4030/https:\/\/static-todo.bitplatform.dev/g' TodoSample/src/Server/TodoSample.Server.Web/Program.Middlewares.cs - - name: Generate CSS/JS files run: dotnet build TodoSample/src/Client/TodoSample.Client.Core/TodoSample.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release @@ -70,11 +66,12 @@ jobs: with: name: server-bundle path: ${{env.DOTNET_ROOT}}/server + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} @@ -128,22 +125,23 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Create project from Boilerplate run: | cd src\Templates\Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ..\..\..\ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --windows --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ..\..\..\ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --windows --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --captcha reCaptcha - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'TodoSample\src\Client\TodoSample.Client.Core\appsettings.json, TodoSample\src\Shared\appsettings.json' + files: 'TodoSample\src\Shared\appsettings.json, TodoSample\src\Client\TodoSample.Client.Core\appsettings.json, TodoSample\src\Client\TodoSample.Client.Windows\appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} - WindowsUpdateSettings.FilesUrl: https://windows-todo.bitplatform.dev + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} + WindowsUpdate.FilesUrl: https://windows-todo.bitplatform.dev ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Set app center secret @@ -189,7 +187,7 @@ jobs: build_blazor_hybrid_android: name: build blazor hybrid (android) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -203,14 +201,14 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Create project from Boilerplate run: | cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha - name: Extract Android signing key from env uses: timheuer/base64-to-file@v1.2 @@ -218,13 +216,21 @@ jobs: fileDir: './TodoSample/src/Client/TodoSample.Client.Maui/' fileName: 'TodoSample.keystore' encodedString: ${{ secrets.ANDROID_RELEASE_KEYSTORE_FILE_BASE64 }} + + - name: Extract Android signing key from env + uses: timheuer/base64-to-file@v1.2 + with: + fileDir: './TodoSample/src/Client/TodoSample.Client.Maui/Platforms/Android' + fileName: 'google-services.json' + encodedString: ${{ secrets.TODO_GOOGLE_SERVICES_JSON_BASE64 }} - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Shared/appsettings.json' + files: 'TodoSample/src/Shared/appsettings.json, TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Client/TodoSample.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Set app center secret @@ -273,7 +279,7 @@ jobs: build_blazor_hybrid_ios: name: build blazor hybrid (iOS-macOS) - runs-on: macos-14 + runs-on: macOS-15 steps: @@ -287,25 +293,26 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - uses: maxim-lobanov/setup-xcode@v1.6.0 with: - xcode-version: '15.4' + xcode-version: '16.0' - name: Create project from Boilerplate run: | cd src/Templates/Boilerplate && dotnet build -c Release dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 dotnet new install Bit.Boilerplate.0.0.0.nupkg - cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage + cd ../../../ && dotnet new bit-bp --name TodoSample --database PostgreSQL --sample Todo --appInsights --appCenter --serverUrl ${{ env.SERVER_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Shared/appsettings.json' + files: 'TodoSample/src/Shared/appsettings.json, TodoSample/src/Client/TodoSample.Client.Core/appsettings.json, TodoSample/src/Client/TodoSample.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} + GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }} ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }} - name: Set app center secret diff --git a/README.md b/README.md index 2693cae43a..986bb2d79e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) ![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) ![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) -[![Nuget downloads](https://img.shields.io/badge/packages_download-4.9M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Nuget downloads](https://img.shields.io/badge/packages_download-5.5M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform "Percentage of issues still open") diff --git a/src/Besql/Bit.Besql/wwwroot/bit-besql.js b/src/Besql/Bit.Besql/wwwroot/bit-besql.js index 70131b54b6..7560d6a59f 100644 --- a/src/Besql/Bit.Besql/wwwroot/bit-besql.js +++ b/src/Besql/Bit.Besql/wwwroot/bit-besql.js @@ -1,5 +1,5 @@ var BitBesql = BitBesql || {}; -BitBesql.version = window['bit-besql version'] = '8.11.0'; +BitBesql.version = window['bit-besql version'] = '8.12.0'; async function synchronizeDbWithCache(file) { diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Bit.Besql.Demo.Client.csproj b/src/Besql/Demo/Bit.Besql.Demo.Client/Bit.Besql.Demo.Client.csproj index aaaaf2c4f2..31dd6dd5bf 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Bit.Besql.Demo.Client.csproj +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Bit.Besql.Demo.Client.csproj @@ -10,7 +10,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModel.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModel.cs deleted file mode 100644 index 5ae652b204..0000000000 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -// <auto-generated /> -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; - -#pragma warning disable 219, 612, 618 -#nullable disable - -namespace Bit.Besql.Demo.Client.Data.CompiledModel -{ - [DbContext(typeof(OfflineDbContext))] - public partial class OfflineDbContextModel : RuntimeModel - { - private static readonly bool _useOldBehavior31751 = - System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751; - - static OfflineDbContextModel() - { - var model = new OfflineDbContextModel(); - - if (_useOldBehavior31751) - { - model.Initialize(); - } - else - { - var thread = new System.Threading.Thread(RunInitialization, 10 * 1024 * 1024); - thread.Start(); - thread.Join(); - - void RunInitialization() - { - model.Initialize(); - } - } - - model.Customize(); - _instance = model; - } - - private static OfflineDbContextModel _instance; - public static IModel Instance => _instance; - - partial void Initialize(); - - partial void Customize(); - } -} diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs deleted file mode 100644 index b9991c6f5c..0000000000 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs +++ /dev/null @@ -1,96 +0,0 @@ -// <auto-generated /> -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; - -#pragma warning disable 219, 612, 618 -#nullable disable - -namespace Bit.Besql.Demo.Client.Data.CompiledModel -{ - public partial class OfflineDbContextModel - { - partial void Initialize() - { - var weatherForecast = WeatherForecastEntityType.Create(this); - - WeatherForecastEntityType.CreateAnnotations(weatherForecast); - - AddAnnotation("ProductVersion", "8.0.0"); - AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel()); - } - - private IRelationalModel CreateRelationalModel() - { - var relationalModel = new RelationalModel(this); - - var weatherForecast = FindEntityType("Bit.Besql.Demo.Client.Model.WeatherForecast")!; - - var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>(); - weatherForecast.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings); - var bitBesqlSampleClientModelWeatherForecastTableBase = new TableBase("Bit.Besql.Demo.Client.Model.WeatherForecast", null, relationalModel); - var dateColumnBase = new ColumnBase<ColumnMappingBase>("Date", "INTEGER", bitBesqlSampleClientModelWeatherForecastTableBase); - bitBesqlSampleClientModelWeatherForecastTableBase.Columns.Add("Date", dateColumnBase); - var idColumnBase = new ColumnBase<ColumnMappingBase>("Id", "INTEGER", bitBesqlSampleClientModelWeatherForecastTableBase); - bitBesqlSampleClientModelWeatherForecastTableBase.Columns.Add("Id", idColumnBase); - var summaryColumnBase = new ColumnBase<ColumnMappingBase>("Summary", "TEXT", bitBesqlSampleClientModelWeatherForecastTableBase) - { - IsNullable = true - }; - bitBesqlSampleClientModelWeatherForecastTableBase.Columns.Add("Summary", summaryColumnBase); - var temperatureCColumnBase = new ColumnBase<ColumnMappingBase>("TemperatureC", "INTEGER", bitBesqlSampleClientModelWeatherForecastTableBase); - bitBesqlSampleClientModelWeatherForecastTableBase.Columns.Add("TemperatureC", temperatureCColumnBase); - relationalModel.DefaultTables.Add("Bit.Besql.Demo.Client.Model.WeatherForecast", bitBesqlSampleClientModelWeatherForecastTableBase); - var bitBesqlSampleClientModelWeatherForecastMappingBase = new TableMappingBase<ColumnMappingBase>(weatherForecast, bitBesqlSampleClientModelWeatherForecastTableBase, true); - bitBesqlSampleClientModelWeatherForecastTableBase.AddTypeMapping(bitBesqlSampleClientModelWeatherForecastMappingBase, false); - defaultTableMappings.Add(bitBesqlSampleClientModelWeatherForecastMappingBase); - RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)idColumnBase, weatherForecast.FindProperty("Id")!, bitBesqlSampleClientModelWeatherForecastMappingBase); - RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)dateColumnBase, weatherForecast.FindProperty("Date")!, bitBesqlSampleClientModelWeatherForecastMappingBase); - RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)summaryColumnBase, weatherForecast.FindProperty("Summary")!, bitBesqlSampleClientModelWeatherForecastMappingBase); - RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)temperatureCColumnBase, weatherForecast.FindProperty("TemperatureC")!, bitBesqlSampleClientModelWeatherForecastMappingBase); - - var tableMappings = new List<TableMapping>(); - weatherForecast.SetRuntimeAnnotation("Relational:TableMappings", tableMappings); - var weatherForecastsTable = new Table("WeatherForecasts", null, relationalModel); - var idColumn = new Column("Id", "INTEGER", weatherForecastsTable); - weatherForecastsTable.Columns.Add("Id", idColumn); - var dateColumn = new Column("Date", "INTEGER", weatherForecastsTable); - weatherForecastsTable.Columns.Add("Date", dateColumn); - var summaryColumn = new Column("Summary", "TEXT", weatherForecastsTable) - { - IsNullable = true - }; - weatherForecastsTable.Columns.Add("Summary", summaryColumn); - var temperatureCColumn = new Column("TemperatureC", "INTEGER", weatherForecastsTable); - weatherForecastsTable.Columns.Add("TemperatureC", temperatureCColumn); - var pK_WeatherForecasts = new UniqueConstraint("PK_WeatherForecasts", weatherForecastsTable, new[] { idColumn }); - weatherForecastsTable.PrimaryKey = pK_WeatherForecasts; - var pK_WeatherForecastsUc = RelationalModel.GetKey(this, - "Bit.Besql.Demo.Client.Model.WeatherForecast", - new[] { "Id" }); - pK_WeatherForecasts.MappedKeys.Add(pK_WeatherForecastsUc); - RelationalModel.GetOrCreateUniqueConstraints(pK_WeatherForecastsUc).Add(pK_WeatherForecasts); - weatherForecastsTable.UniqueConstraints.Add("PK_WeatherForecasts", pK_WeatherForecasts); - var iX_WeatherForecasts_TemperatureC = new TableIndex( - "IX_WeatherForecasts_TemperatureC", weatherForecastsTable, new[] { temperatureCColumn }, false); - var iX_WeatherForecasts_TemperatureCIx = RelationalModel.GetIndex(this, - "Bit.Besql.Demo.Client.Model.WeatherForecast", - new[] { "TemperatureC" }); - iX_WeatherForecasts_TemperatureC.MappedIndexes.Add(iX_WeatherForecasts_TemperatureCIx); - RelationalModel.GetOrCreateTableIndexes(iX_WeatherForecasts_TemperatureCIx).Add(iX_WeatherForecasts_TemperatureC); - weatherForecastsTable.Indexes.Add("IX_WeatherForecasts_TemperatureC", iX_WeatherForecasts_TemperatureC); - relationalModel.Tables.Add(("WeatherForecasts", null), weatherForecastsTable); - var weatherForecastsTableMapping = new TableMapping(weatherForecast, weatherForecastsTable, true); - weatherForecastsTable.AddTypeMapping(weatherForecastsTableMapping, false); - tableMappings.Add(weatherForecastsTableMapping); - RelationalModel.CreateColumnMapping(idColumn, weatherForecast.FindProperty("Id")!, weatherForecastsTableMapping); - RelationalModel.CreateColumnMapping(dateColumn, weatherForecast.FindProperty("Date")!, weatherForecastsTableMapping); - RelationalModel.CreateColumnMapping(summaryColumn, weatherForecast.FindProperty("Summary")!, weatherForecastsTableMapping); - RelationalModel.CreateColumnMapping(temperatureCColumn, weatherForecast.FindProperty("TemperatureC")!, weatherForecastsTableMapping); - return relationalModel.MakeReadOnly(); - } - } -} diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/WeatherForecastEntityType.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/WeatherForecastEntityType.cs deleted file mode 100644 index e1d6226468..0000000000 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/WeatherForecastEntityType.cs +++ /dev/null @@ -1,136 +0,0 @@ -// <auto-generated /> -using System; -using System.Reflection; -using Bit.Besql.Demo.Client.Model; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Json; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#pragma warning disable 219, 612, 618 -#nullable disable - -namespace Bit.Besql.Demo.Client.Data.CompiledModel -{ - internal partial class WeatherForecastEntityType - { - public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) - { - var runtimeEntityType = model.AddEntityType( - "Bit.Besql.Demo.Client.Model.WeatherForecast", - typeof(WeatherForecast), - baseEntityType); - - var id = runtimeEntityType.AddProperty( - "Id", - typeof(int), - propertyInfo: typeof(WeatherForecast).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(WeatherForecast).GetField("<Id>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - valueGenerated: ValueGenerated.OnAdd, - afterSaveBehavior: PropertySaveBehavior.Throw, - sentinel: 0); - id.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - keyComparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - providerValueComparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - mappingInfo: new RelationalTypeMappingInfo( - storeTypeName: "INTEGER")); - - var date = runtimeEntityType.AddProperty( - "Date", - typeof(DateTimeOffset), - propertyInfo: typeof(WeatherForecast).GetProperty("Date", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(WeatherForecast).GetField("<Date>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - valueConverter: new DateTimeOffsetToBinaryConverter()); - date.TypeMapping = LongTypeMapping.Default.Clone( - comparer: new ValueComparer<DateTimeOffset>( - (DateTimeOffset v1, DateTimeOffset v2) => v1.EqualsExact(v2), - (DateTimeOffset v) => v.GetHashCode(), - (DateTimeOffset v) => v), - keyComparer: new ValueComparer<DateTimeOffset>( - (DateTimeOffset v1, DateTimeOffset v2) => v1.EqualsExact(v2), - (DateTimeOffset v) => v.GetHashCode(), - (DateTimeOffset v) => v), - providerValueComparer: new ValueComparer<long>( - (long v1, long v2) => v1 == v2, - (long v) => v.GetHashCode(), - (long v) => v), - mappingInfo: new RelationalTypeMappingInfo( - storeTypeName: "INTEGER"), - converter: new ValueConverter<DateTimeOffset, long>( - (DateTimeOffset v) => DateTimeOffsetToBinaryConverter.ToLong(v), - (long v) => DateTimeOffsetToBinaryConverter.ToDateTimeOffset(v)), - jsonValueReaderWriter: new JsonConvertedValueReaderWriter<DateTimeOffset, long>( - JsonInt64ReaderWriter.Instance, - new ValueConverter<DateTimeOffset, long>( - (DateTimeOffset v) => DateTimeOffsetToBinaryConverter.ToLong(v), - (long v) => DateTimeOffsetToBinaryConverter.ToDateTimeOffset(v)))); - date.SetSentinelFromProviderValue(0L); - - var summary = runtimeEntityType.AddProperty( - "Summary", - typeof(string), - propertyInfo: typeof(WeatherForecast).GetProperty("Summary", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(WeatherForecast).GetField("<Summary>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true, - maxLength: 100); - summary.TypeMapping = SqliteStringTypeMapping.Default; - - var temperatureC = runtimeEntityType.AddProperty( - "TemperatureC", - typeof(int), - propertyInfo: typeof(WeatherForecast).GetProperty("TemperatureC", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(WeatherForecast).GetField("<TemperatureC>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: 0); - temperatureC.TypeMapping = IntTypeMapping.Default.Clone( - comparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - keyComparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - providerValueComparer: new ValueComparer<int>( - (int v1, int v2) => v1 == v2, - (int v) => v, - (int v) => v), - mappingInfo: new RelationalTypeMappingInfo( - storeTypeName: "INTEGER")); - - var key = runtimeEntityType.AddKey( - new[] { id }); - runtimeEntityType.SetPrimaryKey(key); - - var index = runtimeEntityType.AddIndex( - new[] { temperatureC }); - - return runtimeEntityType; - } - - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "WeatherForecasts"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); - - Customize(runtimeEntityType); - } - - static partial void Customize(RuntimeEntityType runtimeEntityType); - } -} diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/OfflineDbContext.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/OfflineDbContext.cs index 4f44dec53b..94d61448e8 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/OfflineDbContext.cs +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/OfflineDbContext.cs @@ -1,5 +1,4 @@ -using Bit.Besql.Demo.Client.Data.CompiledModel; -using Bit.Besql.Demo.Client.Model; +using Bit.Besql.Demo.Client.Model; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -30,7 +29,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder - .UseModel(OfflineDbContextModel.Instance) // use generated compiled model in order to make db context optimized + // .UseModel(OfflineDbContextModel.Instance) // use generated compiled model in order to make db context optimized .UseSqlite("Data Source=Offline-ClientDb.db"); base.OnConfiguring(optionsBuilder); diff --git a/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj b/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj index 82cf18dea9..c9a750a921 100644 --- a/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj +++ b/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj @@ -8,7 +8,7 @@ <ItemGroup> <ProjectReference Include="..\Bit.Besql.Demo.Client\Bit.Besql.Demo.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> </ItemGroup> <ItemGroup> @@ -19,11 +19,11 @@ and open Nuget Package Manager Console, and select `Bit.Besql.Demo` project as default project Then run either Add-Migration MigrationName -OutputDir Data\Migrations or Optimize-DbContext -OutputDir Data/CompiledModel commands. --> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8"> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8"> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/src/Bit.Build.props b/src/Bit.Build.props index 6a21ccbb44..6efc6d1da5 100644 --- a/src/Bit.Build.props +++ b/src/Bit.Build.props @@ -25,7 +25,7 @@ <PackageProjectUrl>https://github.com/bitfoundation/bitplatform</PackageProjectUrl> <PackageIconUrl>https://avatars.githubusercontent.com/u/22663390</PackageIconUrl> - <ReleaseVersion>8.11.0</ReleaseVersion> + <ReleaseVersion>8.12.0</ReleaseVersion> <PackageReleaseNotes>https://github.com/bitfoundation/bitplatform/releases/tag/v-$(ReleaseVersion)</PackageReleaseNotes> <PackageVersion>$(ReleaseVersion)</PackageVersion> diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/Bit.BlazorUI.Assets.csproj b/src/BlazorUI/Bit.BlazorUI.Assets/Bit.BlazorUI.Assets.csproj index 55c1b88153..5dc6f0849e 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/Bit.BlazorUI.Assets.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Assets/Bit.BlazorUI.Assets.csproj @@ -33,7 +33,7 @@ </Target> <Target Name="BuildCss" Inputs="@(ScssFiles)" Outputs="wwwroot/styles/bit.blazorui.assets.css"> - <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.assets.scss:wwwroot/styles/bit.blazorui.assets.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.assets.scss:wwwroot/styles/bit.blazorui.assets.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json index d8e763d495..3eaa56c719 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json @@ -5,32 +5,293 @@ "packages": { "": { "devDependencies": { - "sass": "1.77.8" + "sass": "1.80.5" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -38,6 +299,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -46,27 +308,32 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/fill-range": { @@ -74,6 +341,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -81,55 +349,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -139,6 +370,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -151,24 +383,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -177,24 +423,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -219,6 +469,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package.json b/src/BlazorUI/Bit.BlazorUI.Assets/package.json index ea5cb1f7da..86b9c82cc5 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.77.8" + "sass": "1.80.5" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj b/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj index b5fb92fa65..e2d356b695 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj @@ -58,9 +58,9 @@ </ItemGroup> <Target Name="BeforeBuildTasks" AfterTargets="CoreCompile" Condition="'$(TargetFramework)' == 'net8.0'"> - <CallTarget Targets="InstallNodejsDependencies"/> - <CallTarget Targets="BuildJavaScript"/> - <CallTarget Targets="BuildCss"/> + <CallTarget Targets="InstallNodejsDependencies" /> + <CallTarget Targets="BuildJavaScript" /> + <CallTarget Targets="BuildCss" /> </Target> <Target Name="InstallNodejsDependencies" Inputs="package.json" Outputs="node_modules\.package-lock.json"> @@ -73,7 +73,7 @@ </Target> <Target Name="BuildCss" Inputs="@(ScssFiles)" Outputs="wwwroot/styles/bit.blazorui.extras.css"> - <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.extras.scss:wwwroot/styles/bit.blazorui.extras.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.extras.scss:wwwroot/styles/bit.blazorui.extras.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs index af56d99829..064eadb1fa 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs @@ -122,15 +122,15 @@ protected override async Task OnAfterRenderAsync(bool firstRender) scripts.AddRange(DateAdapterScripts); } - await _js.InitChartJs(scripts); + await _js.BitChartJsInitChartJs(scripts); - await _js.SetupChart(Config); + await _js.BitChartJsSetupChart(Config); await SetupCompletedCallback.InvokeAsync(this); } else { - await _js.SetupChart(Config); + await _js.BitChartJsSetupChart(Config); } } @@ -142,7 +142,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) /// </summary> public Task Update() { - return _js.UpdateChart(Config).AsTask(); + return _js.BitChartJsUpdateChart(Config).AsTask(); } public async ValueTask DisposeAsync() @@ -158,7 +158,7 @@ protected virtual async ValueTask DisposeAsync(bool disposing) try { - await _js.RemoveChart(Config?.CanvasId); + await _js.BitChartJsRemoveChart(Config?.CanvasId); } catch (JSDisconnectedException) { } // we can ignore this exception here } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/TypeScript/BitChart.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/TypeScript/BitChart.ts rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/TypeScript/types/Chartjs.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Chartjs.d.ts similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/TypeScript/types/Chartjs.d.ts rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Chartjs.d.ts diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartDelegateHandler.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartDelegateHandler.cs similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartDelegateHandler.cs rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartDelegateHandler.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartIgnoreCallbackValueAttribute.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartIgnoreCallbackValueAttribute.cs similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartIgnoreCallbackValueAttribute.cs rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartIgnoreCallbackValueAttribute.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartJavascriptHandler.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJavascriptHandler.cs similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartJavascriptHandler.cs rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJavascriptHandler.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartJsInterop.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs similarity index 95% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartJsInterop.cs rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs index ae47a5a9b4..789ebd6e2f 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/BitChartJsInterop.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs @@ -20,12 +20,12 @@ internal static class BitChartJsInterop Converters = { new IsoDateTimeConverter() } }; - public static ValueTask InitChartJs(this IJSRuntime jsRuntime, IEnumerable<string> scripts) + public static ValueTask BitChartJsInitChartJs(this IJSRuntime jsRuntime, IEnumerable<string> scripts) { return jsRuntime.InvokeVoidAsync("BitBlazorUI.BitChart.initChartJs", scripts); } - public static ValueTask RemoveChart(this IJSRuntime jsRuntime, string canvasId) + public static ValueTask BitChartJsRemoveChart(this IJSRuntime jsRuntime, string canvasId) { return jsRuntime.InvokeVoidAsync("BitBlazorUI.BitChart.removeChart", canvasId); } @@ -36,13 +36,28 @@ public static ValueTask RemoveChart(this IJSRuntime jsRuntime, string canvasId) /// <param name="jsRuntime"></param> /// <param name="chartConfig">The config for the new chart.</param> /// <returns></returns> - public static ValueTask<bool> SetupChart(this IJSRuntime jsRuntime, BitChartConfigBase chartConfig) + public static ValueTask<bool> BitChartJsSetupChart(this IJSRuntime jsRuntime, BitChartConfigBase chartConfig) { var dynParam = StripNulls(chartConfig); Dictionary<string, object> param = ConvertExpandoObjectToDictionary(dynParam); return jsRuntime.InvokeAsync<bool>("BitBlazorUI.BitChart.setupChart", param); } + /// <summary> + /// Update an existing chart. Make sure that the Chart with this <see cref="BitChartConfigBase.CanvasId"/> already exists. + /// </summary> + /// <param name="jsRuntime"></param> + /// <param name="chartConfig">The updated config of the chart you want to update.</param> + /// <returns></returns> + public static ValueTask<bool> BitChartJsUpdateChart(this IJSRuntime jsRuntime, BitChartConfigBase chartConfig) + { + var dynParam = StripNulls(chartConfig); + Dictionary<string, object> param = ConvertExpandoObjectToDictionary(dynParam); + return jsRuntime.InvokeAsync<bool>("BitBlazorUI.BitChart.updateChart", param); + } + + + /// <summary> /// This method is specifically used to convert an <see cref="ExpandoObject"/> with a Tree structure to a <c>Dictionary<string, object></c>. /// </summary> @@ -85,19 +100,6 @@ private static Dictionary<string, object> RecursivelyConvertIDictToDict(IDiction } ); - /// <summary> - /// Update an existing chart. Make sure that the Chart with this <see cref="BitChartConfigBase.CanvasId"/> already exists. - /// </summary> - /// <param name="jsRuntime"></param> - /// <param name="chartConfig">The updated config of the chart you want to update.</param> - /// <returns></returns> - public static ValueTask<bool> UpdateChart(this IJSRuntime jsRuntime, BitChartConfigBase chartConfig) - { - var dynParam = StripNulls(chartConfig); - Dictionary<string, object> param = ConvertExpandoObjectToDictionary(dynParam); - return jsRuntime.InvokeAsync<bool>("BitBlazorUI.BitChart.updateChart", param); - } - /// <summary> /// Returns an object that is equivalent to the given parameter but without any null members AND it preserves <see cref="IBitChartMethodHandler"/>s intact. /// <para>Preserving <see cref="IBitChartMethodHandler"/> members is important because they might be <see cref="BitChartDelegateHandler{T}"/> instances which contain diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/IBitChartMethodHandler.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/IBitChartMethodHandler.cs similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/Interop/IBitChartMethodHandler.cs rename to src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/IBitChartMethodHandler.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs index 58a41f2b63..d8a181b483 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs @@ -416,6 +416,12 @@ public async ValueTask DisposeAsync() // The JS side may routinely be gone already if the reason we're disposing is that // the client disconnected. This is not an error. } + catch(JSException ex) + { + // it seems it's safe to just ignore this exception here. + // otherwise it will blow up the MAUI app in a page refresh for example. + Console.WriteLine(ex.Message); + } } private void CloseColumnOptions() diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.scss index ca30743ff1..ec3be56fd7 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.scss @@ -32,12 +32,12 @@ .col-sort-asc .sort-indicator:before, .col-sort-desc .sort-indicator:before { - //background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M 2 3.25 L 12 20.75 L 22 3.25 L 12 10 z" /></svg>'); - content: "\E96E"; + content: "\E96F"; font-style: normal; font-weight: normal; display: inline-block; - font-family: 'Fabric MDL2 bit BlazorUI' !important; + transform: rotate(90deg); + font-family: 'Fabric MDL2 bit BlazorUI Extras'; } .col-sort-desc .sort-indicator { @@ -53,7 +53,7 @@ font-style: normal; font-weight: normal; display: inline-block; - font-family: 'Fabric MDL2 bit BlazorUI' !important; + font-family: 'Fabric MDL2 bit BlazorUI Extras'; } .col-options { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/Pagination/BitDataGridPaginator.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/Pagination/BitDataGridPaginator.scss index a5c88d6333..04cda8f09b 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/Pagination/BitDataGridPaginator.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/Pagination/BitDataGridPaginator.scss @@ -37,22 +37,20 @@ .go-first:before, .go-last:before { - //background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(90) scale(0.8)" transform-origin="12 12"><path d="m 2,1.5 l 10,17.5 l 10,-17.5 l -10,7.75 l -10,-7.75 z"/><rect height="2" width="20" y="20.5" x="2"/></g></svg>'); content: "\F371"; font-style: normal; font-weight: normal; display: inline-block; - font-family: 'Fabric MDL2 bit BlazorUI' !important; + font-family: 'Fabric MDL2 bit BlazorUI Extras'; } .go-previous:before, .go-next:before { - //background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(90)" transform-origin="12 12"><path d="M 2 3.25 L 12 20.75 L 22 3.25 L 12 11 z" /></g></svg>'); content: "\E96F"; font-style: normal; font-weight: normal; display: inline-block; - font-family: 'Fabric MDL2 bit BlazorUI' !important; + font-family: 'Fabric MDL2 bit BlazorUI Extras'; } .go-next, .go-last { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_layer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_layer.d.ts new file mode 100644 index 0000000000..7d5b254852 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_layer.d.ts @@ -0,0 +1,215 @@ +declare type AnnotationElementParameters = { + data: Object; + layer: HTMLDivElement; + linkService: IPDFLinkService; + downloadManager?: IDownloadManager | undefined; + annotationStorage?: AnnotationStorage | undefined; + /** + * - Path for image resources, mainly + * for annotation icons. Include trailing slash. + */ + imageResourcesPath?: string | undefined; + renderForms: boolean; + svgFactory: Object; + enableScripting?: boolean | undefined; + hasJSActions?: boolean | undefined; + fieldObjects?: Object | undefined; +}; +declare type AnnotationLayerParameters = { + viewport: PageViewport; + div: HTMLDivElement; + annotations: any[]; + page: PDFPageProxy; + linkService: IPDFLinkService; + downloadManager?: IDownloadManager | undefined; + annotationStorage?: AnnotationStorage | undefined; + /** + * - Path for image resources, mainly + * for annotation icons. Include trailing slash. + */ + imageResourcesPath?: string | undefined; + renderForms: boolean; + /** + * - Enable embedded script execution. + */ + enableScripting?: boolean | undefined; + /** + * - Some fields have JS actions. + * The default value is `false`. + */ + hasJSActions?: boolean | undefined; + fieldObjects?: { + [x: string]: Object[]; + } | null | undefined; + annotationCanvasMap?: Map<string, HTMLCanvasElement> | undefined; + accessibilityManager?: TextAccessibilityManager | undefined; + annotationEditorUIManager?: AnnotationEditorUIManager; + structTreeLayer?: StructTreeLayerBuilder | undefined; +}; +/** + * @typedef {Object} AnnotationLayerParameters + * @property {PageViewport} viewport + * @property {HTMLDivElement} div + * @property {Array} annotations + * @property {PDFPageProxy} page + * @property {IPDFLinkService} linkService + * @property {IDownloadManager} [downloadManager] + * @property {AnnotationStorage} [annotationStorage] + * @property {string} [imageResourcesPath] - Path for image resources, mainly + * for annotation icons. Include trailing slash. + * @property {boolean} renderForms + * @property {boolean} [enableScripting] - Enable embedded script execution. + * @property {boolean} [hasJSActions] - Some fields have JS actions. + * The default value is `false`. + * @property {Object<string, Array<Object>> | null} [fieldObjects] + * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] + * @property {TextAccessibilityManager} [accessibilityManager] + * @property {AnnotationEditorUIManager} [annotationEditorUIManager] + * @property {StructTreeLayerBuilder} [structTreeLayer] + */ +/** + * Manage the layer containing all the annotations. + */ +declare class AnnotationLayer { + constructor({ div, accessibilityManager, annotationCanvasMap, annotationEditorUIManager, page, viewport, structTreeLayer, }: { + div: any; + accessibilityManager: any; + annotationCanvasMap: any; + annotationEditorUIManager: any; + page: any; + viewport: any; + structTreeLayer: any; + }); + div: any; + page: any; + viewport: any; + zIndex: number; + _annotationEditorUIManager: any; + popupShow: any[] | undefined; + hasEditableAnnotations(): boolean; + /** + * Render a new annotation layer with all annotation elements. + * + * @param {AnnotationLayerParameters} params + * @memberof AnnotationLayer + */ + render(params: AnnotationLayerParameters): Promise<void>; + /** + * Update the annotation elements on existing annotation layer. + * + * @param {AnnotationLayerParameters} viewport + * @memberof AnnotationLayer + */ + update({ viewport }: AnnotationLayerParameters): void; + getEditableAnnotations(): any[]; + getEditableAnnotation(id: any): any; +} +declare class FreeTextAnnotationElement extends AnnotationElement { + constructor(parameters: any); + textContent: any; + textPosition: any; + annotationEditorType: number; + render(): HTMLElement | undefined; +} +declare class HighlightAnnotationElement extends AnnotationElement { + constructor(parameters: any); + annotationEditorType: number; + render(): HTMLElement | undefined; +} +declare class InkAnnotationElement extends AnnotationElement { + constructor(parameters: any); + containerClassName: string; + svgElementName: string; + annotationEditorType: number; + render(): HTMLElement | undefined; + getElementsToTriggerPopup(): any[]; +} +declare class StampAnnotationElement extends AnnotationElement { + constructor(parameters: any); + annotationEditorType: number; + render(): HTMLElement | undefined; +} +declare class AnnotationElement { + static _hasPopupData({ titleObj, contentsObj, richText }: { + titleObj: any; + contentsObj: any; + richText: any; + }): boolean; + constructor(parameters: any, { isRenderable, ignoreBorder, createQuadrilaterals, }?: { + isRenderable?: boolean | undefined; + ignoreBorder?: boolean | undefined; + createQuadrilaterals?: boolean | undefined; + }); + isRenderable: boolean; + data: any; + layer: any; + linkService: any; + downloadManager: any; + imageResourcesPath: any; + renderForms: any; + svgFactory: any; + annotationStorage: any; + enableScripting: any; + hasJSActions: any; + _fieldObjects: any; + parent: any; + container: HTMLElement | undefined; + get _isEditable(): any; + get hasPopupData(): boolean; + updateEdited(params: any): void; + resetEdited(): void; + /** + * Create an empty container for the annotation's HTML element. + * + * @private + * @param {boolean} ignoreBorder + * @memberof AnnotationElement + * @returns {HTMLElement} A section element. + */ + private _createContainer; + setRotation(angle: any, container?: HTMLElement | undefined): void; + get _commonActions(): any; + _dispatchEventFromSandbox(actions: any, jsEvent: any): void; + _setDefaultPropertiesFromJS(element: any): void; + /** + * Create quadrilaterals from the annotation's quadpoints. + * + * @private + * @memberof AnnotationElement + */ + private _createQuadrilaterals; + /** + * Create a popup for the annotation's HTML element. This is used for + * annotations that do not have a Popup entry in the dictionary, but + * are of a type that works with popups (such as Highlight annotations). + * + * @private + * @memberof AnnotationElement + */ + private _createPopup; + /** + * Render the annotation's HTML element(s). + * + * @public + * @memberof AnnotationElement + */ + public render(): void; + /** + * @private + * @returns {Array} + */ + private _getElementsByName; + show(): void; + hide(): void; + /** + * Get the HTML element(s) which can trigger a popup when clicked or hovered. + * + * @public + * @memberof AnnotationElement + * @returns {Array<HTMLElement>|HTMLElement} An array of elements or an + * element. + */ + public getElementsToTriggerPopup(): Array<HTMLElement> | HTMLElement; + addHighlightArea(): void; + _editOnDoubleClick(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_storage.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_storage.d.ts new file mode 100644 index 0000000000..b3a7430e6d --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/annotation_storage.d.ts @@ -0,0 +1,97 @@ +/** + * Key/value storage for annotation data in forms. + */ +declare class AnnotationStorage { + onSetModified: any; + onResetModified: any; + onAnnotationEditor: any; + /** + * Get the value for a given key if it exists, or return the default value. + * @param {string} key + * @param {Object} defaultValue + * @returns {Object} + */ + getValue(key: string, defaultValue: Object): Object; + /** + * Get the value for a given key. + * @param {string} key + * @returns {Object} + */ + getRawValue(key: string): Object; + /** + * Remove a value from the storage. + * @param {string} key + */ + remove(key: string): void; + /** + * Set the value for a given key + * @param {string} key + * @param {Object} value + */ + setValue(key: string, value: Object): void; + /** + * Check if the storage contains the given key. + * @param {string} key + * @returns {boolean} + */ + has(key: string): boolean; + /** + * @returns {Object | null} + */ + getAll(): Object | null; + /** + * @param {Object} obj + */ + setAll(obj: Object): void; + get size(): number; + resetModified(): void; + /** + * @returns {PrintAnnotationStorage} + */ + get print(): PrintAnnotationStorage; + /** + * PLEASE NOTE: Only intended for usage within the API itself. + * @ignore + */ + get serializable(): Readonly<{ + map: null; + hash: ""; + transfer: undefined; + }> | { + map: Map<any, any>; + hash: string; + transfer: any[]; + }; + get editorStats(): any; + resetModifiedIds(): void; + /** + * @returns {{ids: Set<string>, hash: string}} + */ + get modifiedIds(): { + ids: Set<string>; + hash: string; + }; +} +/** + * A special `AnnotationStorage` for use during printing, where the serializable + * data is *frozen* upon initialization, to prevent scripting from modifying its + * contents. (Necessary since printing is triggered synchronously in browsers.) + */ +declare class PrintAnnotationStorage extends AnnotationStorage { + constructor(parent: any); + /** + * PLEASE NOTE: Only intended for usage within the API itself. + * @ignore + */ + get serializable(): { + map: any; + hash: any; + transfer: any; + }; + get modifiedIds(): any; +} +declare const SerializableEmpty: Readonly<{ + map: null; + hash: ""; + transfer: undefined; +}>; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/api.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/api.d.ts new file mode 100644 index 0000000000..13b4c3a287 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/api.d.ts @@ -0,0 +1,1509 @@ +declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; +declare type RefProxy = { + num: number; + gen: number; +}; +/** + * Document initialization / loading parameters object. + */ +declare type DocumentInitParameters = { + /** + * - The URL of the PDF. + */ + url?: string | URL | undefined; + /** + * - + * Binary PDF data. + * Use TypedArrays (Uint8Array) to improve the memory usage. If PDF data is + * BASE64-encoded, use `atob()` to convert it to a binary string first. + * + * NOTE: If TypedArrays are used they will generally be transferred to the + * worker-thread. This will help reduce main-thread memory usage, however + * it will take ownership of the TypedArrays. + */ + data?: string | number[] | ArrayBuffer | TypedArray | undefined; + /** + * - Basic authentication headers. + */ + httpHeaders?: Object | undefined; + /** + * - Indicates whether or not + * cross-site Access-Control requests should be made using credentials such + * as cookies or authorization headers. The default is `false`. + */ + withCredentials?: boolean | undefined; + /** + * - For decrypting password-protected PDFs. + */ + password?: string | undefined; + /** + * - The PDF file length. It's used for progress + * reports and range requests operations. + */ + length?: number | undefined; + /** + * - Allows for using a custom range + * transport implementation. + */ + range?: PDFDataRangeTransport | undefined; + /** + * - Specify maximum number of bytes fetched + * per range request. The default value is {@link DEFAULT_RANGE_CHUNK_SIZE}. + */ + rangeChunkSize?: number | undefined; + /** + * - The worker that will be used for loading and + * parsing the PDF data. + */ + worker?: PDFWorker | undefined; + /** + * - Controls the logging level; the constants + * from {@link VerbosityLevel} should be used. + */ + verbosity?: number | undefined; + /** + * - The base URL of the document, used when + * attempting to recover valid absolute URLs for annotations, and outline + * items, that (incorrectly) only specify relative URLs. + */ + docBaseUrl?: string | undefined; + /** + * - The URL where the predefined Adobe CMaps are + * located. Include the trailing slash. + */ + cMapUrl?: string | undefined; + /** + * - Specifies if the Adobe CMaps are binary + * packed or not. The default value is `true`. + */ + cMapPacked?: boolean | undefined; + /** + * - The factory that will be used when + * reading built-in CMap files. Providing a custom factory is useful for + * environments without Fetch API or `XMLHttpRequest` support, such as + * Node.js. The default value is {DOMCMapReaderFactory}. + */ + CMapReaderFactory?: Object | undefined; + /** + * - When `true`, fonts that aren't + * embedded in the PDF document will fallback to a system font. + * The default value is `true` in web environments and `false` in Node.js; + * unless `disableFontFace === true` in which case this defaults to `false` + * regardless of the environment (to prevent completely broken fonts). + */ + useSystemFonts?: boolean | undefined; + /** + * - The URL where the standard font + * files are located. Include the trailing slash. + */ + standardFontDataUrl?: string | undefined; + /** + * - The factory that will be used + * when reading the standard font files. Providing a custom factory is useful + * for environments without Fetch API or `XMLHttpRequest` support, such as + * Node.js. The default value is {DOMStandardFontDataFactory}. + */ + StandardFontDataFactory?: Object | undefined; + /** + * - Enable using the Fetch API in the + * worker-thread when reading CMap and standard font files. When `true`, + * the `CMapReaderFactory` and `StandardFontDataFactory` options are ignored. + * The default value is `true` in web environments and `false` in Node.js. + */ + useWorkerFetch?: boolean | undefined; + /** + * - Reject certain promises, e.g. + * `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated + * PDF data cannot be successfully parsed, instead of attempting to recover + * whatever possible of the data. The default value is `false`. + */ + stopAtErrors?: boolean | undefined; + /** + * - The maximum allowed image size in total + * pixels, i.e. width * height. Images above this value will not be rendered. + * Use -1 for no limit, which is also the default value. + */ + maxImageSize?: number | undefined; + /** + * - Determines if we can evaluate strings + * as JavaScript. Primarily used to improve performance of PDF functions. + * The default value is `true`. + */ + isEvalSupported?: boolean | undefined; + /** + * - Determines if we can use + * `OffscreenCanvas` in the worker. Primarily used to improve performance of + * image conversion/rendering. + * The default value is `true` in web environments and `false` in Node.js. + */ + isOffscreenCanvasSupported?: boolean | undefined; + /** + * - The integer value is used to + * know when an image must be resized (uses `OffscreenCanvas` in the worker). + * If it's -1 then a possibly slow algorithm is used to guess the max value. + */ + canvasMaxAreaInBytes?: number | undefined; + /** + * - By default fonts are converted to + * OpenType fonts and loaded via the Font Loading API or `@font-face` rules. + * If disabled, fonts will be rendered using a built-in font renderer that + * constructs the glyphs with primitive path commands. + * The default value is `false` in web environments and `true` in Node.js. + */ + disableFontFace?: boolean | undefined; + /** + * - Include additional properties, + * which are unused during rendering of PDF documents, when declareing the + * parsed font data from the worker-thread. This may be useful for debugging + * purposes (and backwards compatibility), but note that it will lead to + * increased memory usage. The default value is `false`. + */ + fontExtraProperties?: boolean | undefined; + /** + * - Render Xfa forms if any. + * The default value is `false`. + */ + enableXfa?: boolean | undefined; + /** + * - Specify an explicit document + * context to create elements with and to load resources, such as fonts, + * into. Defaults to the current document. + */ + ownerDocument?: HTMLDocument | undefined; + /** + * - Disable range request loading of PDF + * files. When enabled, and if the server supports partial content requests, + * then the PDF will be fetched in chunks. The default value is `false`. + */ + disableRange?: boolean | undefined; + /** + * - Disable streaming of PDF file data. + * By default PDF.js attempts to load PDF files in chunks. The default value + * is `false`. + */ + disableStream?: boolean | undefined; + /** + * - Disable pre-fetching of PDF file + * data. When range requests are enabled PDF.js will automatically keep + * fetching more data even if it isn't needed to display the current page. + * The default value is `false`. + * + * NOTE: It is also necessary to disable streaming, see above, in order for + * disabling of pre-fetching to work correctly. + */ + disableAutoFetch?: boolean | undefined; + /** + * - Enables special hooks for debugging PDF.js + * (see `web/debugger.js`). The default value is `false`. + */ + pdfBug?: boolean | undefined; + /** + * - The factory that will be used when + * creating canvases. The default value is {DOMCanvasFactory}. + */ + CanvasFactory?: Object | undefined; + /** + * - The factory that will be used to + * create SVG filters when rendering some images on the main canvas. + * The default value is {DOMFilterFactory}. + */ + FilterFactory?: Object | undefined; + /** + * - Enables hardware acceleration for + * rendering. The default value is `false`. + */ + enableHWA?: boolean | undefined; +}; +declare type OnProgressParameters = { + /** + * - Currently loaded number of bytes. + */ + loaded: number; + /** + * - Total number of bytes in the PDF file. + */ + total: number; +}; +/** + * Page getViewport parameters. + */ +declare type GetViewportParameters = { + /** + * - The desired scale of the viewport. + */ + scale: number; + /** + * - The desired rotation, in degrees, of + * the viewport. If omitted it defaults to the page rotation. + */ + rotation?: number | undefined; + /** + * - The horizontal, i.e. x-axis, offset. + * The default value is `0`. + */ + offsetX?: number | undefined; + /** + * - The vertical, i.e. y-axis, offset. + * The default value is `0`. + */ + offsetY?: number | undefined; + /** + * - If true, the y-axis will not be + * flipped. The default value is `false`. + */ + dontFlip?: boolean | undefined; +}; +/** + * Page getTextContent parameters. + */ +declare type getTextContentParameters = { + /** + * - When true include marked + * content items in the items array of TextContent. The default is `false`. + */ + includeMarkedContent?: boolean | undefined; + /** + * - When true the text is *not* + * normalized in the worker-thread. The default is `false`. + */ + disableNormalization?: boolean | undefined; +}; +/** + * Page text content. + */ +declare type TextContent = { + /** + * - Array of + * {@link TextItem} and {@link TextMarkedContent} objects. TextMarkedContent + * items are included when includeMarkedContent is true. + */ + items: Array<TextItem | TextMarkedContent>; + /** + * - {@link TextStyle} objects, + * indexed by font name. + */ + styles: { + [x: string]: TextStyle; + }; + /** + * - The document /Lang attribute. + */ + lang: string | null; +}; +/** + * Page text content part. + */ +declare type TextItem = { + /** + * - Text content. + */ + str: string; + /** + * - Text direction: 'ttb', 'ltr' or 'rtl'. + */ + dir: string; + /** + * - Transformation matrix. + */ + transform: Array<any>; + /** + * - Width in device space. + */ + width: number; + /** + * - Height in device space. + */ + height: number; + /** + * - Font name used by PDF.js for converted font. + */ + fontName: string; + /** + * - Indicating if the text content is followed by a + * line-break. + */ + hasEOL: boolean; +}; +/** + * Page text marked content part. + */ +declare type TextMarkedContent = { + /** + * - Either 'beginMarkedContent', + * 'beginMarkedContentProps', or 'endMarkedContent'. + */ + type: string; + /** + * - The marked content identifier. Only used for type + * 'beginMarkedContentProps'. + */ + id: string; +}; +/** + * Text style. + */ +declare type TextStyle = { + /** + * - Font ascent. + */ + ascent: number; + /** + * - Font descent. + */ + descent: number; + /** + * - Whether or not the text is in vertical mode. + */ + vertical: boolean; + /** + * - The possible font family. + */ + fontFamily: string; +}; +/** + * Page annotation parameters. + */ +declare type GetAnnotationsParameters = { + /** + * - Determines the annotations that are fetched, + * can be 'display' (viewable annotations), 'print' (printable annotations), + * or 'any' (all annotations). The default value is 'display'. + */ + intent?: string | undefined; +}; +/** + * Page render parameters. + */ +declare type RenderParameters = { + /** + * - A 2D context of a DOM + * Canvas object. + */ + canvasContext: CanvasRenderingContext2D; + /** + * - Rendering viewport obtained by calling + * the `PDFPageProxy.getViewport` method. + */ + viewport: PageViewport; + /** + * - Rendering intent, can be 'display', 'print', + * or 'any'. The default value is 'display'. + */ + intent?: string | undefined; + /** + * Controls which annotations are rendered + * onto the canvas, for annotations with appearance-data; the values from + * {@link AnnotationMode} should be used. The following values are supported: + * - `AnnotationMode.DISABLE`, which disables all annotations. + * - `AnnotationMode.ENABLE`, which includes all possible annotations (thus + * it also depends on the `intent`-option, see above). + * - `AnnotationMode.ENABLE_FORMS`, which excludes annotations that contain + * interactive form elements (those will be rendered in the display layer). + * - `AnnotationMode.ENABLE_STORAGE`, which includes all possible annotations + * (as above) but where interactive form elements are updated with data + * from the {@link AnnotationStorage}-instance; useful e.g. for printing. + * The default value is `AnnotationMode.ENABLE`. + */ + annotationMode?: number | undefined; + /** + * - Additional transform, applied just + * before viewport transform. + */ + transform?: any[] | undefined; + /** + * - Background + * to use for the canvas. + * Any valid `canvas.fillStyle` can be used: a `DOMString` parsed as CSS + * <color> value, a `CanvasGradient` object (a linear or radial gradient) or + * a `CanvasPattern` object (a repetitive image). The default value is + * 'rgb(255,255,255)'. + * + * NOTE: This option may be partially, or completely, ignored when the + * `pageColors`-option is used. + */ + background?: string | CanvasGradient | CanvasPattern | undefined; + /** + * - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + */ + pageColors?: Object | undefined; + /** + * - + * A promise that should resolve with an {@link OptionalContentConfig}created from `PDFDocumentProxy.getOptionalContentConfig`. If `null`, + * the configuration will be fetched automatically with the default visibility + * states set. + */ + optionalContentConfigPromise?: Promise<OptionalContentConfig> | undefined; + /** + * - Map some + * annotation ids with canvases used to render them. + */ + annotationCanvasMap?: Map<string, HTMLCanvasElement> | undefined; + printAnnotationStorage?: PrintAnnotationStorage | undefined; + /** + * - Render the page in editing mode. + */ + isEditing?: boolean | undefined; +}; +/** + * Page getOperatorList parameters. + */ +declare type GetOperatorListParameters = { + /** + * - Rendering intent, can be 'display', 'print', + * or 'any'. The default value is 'display'. + */ + intent?: string | undefined; + /** + * Controls which annotations are included + * in the operatorList, for annotations with appearance-data; the values from + * {@link AnnotationMode} should be used. The following values are supported: + * - `AnnotationMode.DISABLE`, which disables all annotations. + * - `AnnotationMode.ENABLE`, which includes all possible annotations (thus + * it also depends on the `intent`-option, see above). + * - `AnnotationMode.ENABLE_FORMS`, which excludes annotations that contain + * interactive form elements (those will be rendered in the display layer). + * - `AnnotationMode.ENABLE_STORAGE`, which includes all possible annotations + * (as above) but where interactive form elements are updated with data + * from the {@link AnnotationStorage}-instance; useful e.g. for printing. + * The default value is `AnnotationMode.ENABLE`. + */ + annotationMode?: number | undefined; + printAnnotationStorage?: PrintAnnotationStorage | undefined; + /** + * - Render the page in editing mode. + */ + isEditing?: boolean | undefined; +}; +/** + * Structure tree node. The root node will have a role "Root". + */ +declare type StructTreeNode = { + /** + * - Array of + * {@link StructTreeNode} and {@link StructTreeContent} objects. + */ + children: Array<StructTreeNode | StructTreeContent>; + /** + * - element's role, already mapped if a role map exists + * in the PDF. + */ + role: string; +}; +/** + * Structure tree content. + */ +declare type StructTreeContent = { + /** + * - either "content" for page and stream structure + * elements or "object" for object references. + */ + type: string; + /** + * - unique id that will map to the text layer. + */ + id: string; +}; +/** + * PDF page operator list. + */ +declare type PDFOperatorList = { + /** + * - Array containing the operator functions. + */ + fnArray: Array<number>; + /** + * - Array containing the arguments of the + * functions. + */ + argsArray: Array<any>; +}; +declare type PDFWorkerParameters = { + /** + * - The name of the worker. + */ + name?: string | undefined; + /** + * - The `workerPort` object. + */ + port?: Worker | undefined; + /** + * - Controls the logging level; + * the constants from {@link VerbosityLevel} should be used. + */ + verbosity?: number | undefined; +}; +/** @type {string} */ +declare const build: string; +declare const DefaultCanvasFactory: typeof NodeCanvasFactory; +declare const DefaultCMapReaderFactory: typeof NodeCMapReaderFactory; +declare const DefaultFilterFactory: typeof DOMFilterFactory | typeof NodeFilterFactory; +declare const DefaultStandardFontDataFactory: typeof NodeStandardFontDataFactory; +/** + * @typedef { Int8Array | Uint8Array | Uint8ClampedArray | + * Int16Array | Uint16Array | + * Int32Array | Uint32Array | Float32Array | + * Float64Array + * } TypedArray + */ +/** + * @typedef {Object} RefProxy + * @property {number} num + * @property {number} gen + */ +/** + * Document initialization / loading parameters object. + * + * @typedef {Object} DocumentInitParameters + * @property {string | URL} [url] - The URL of the PDF. + * @property {TypedArray | ArrayBuffer | Array<number> | string} [data] - + * Binary PDF data. + * Use TypedArrays (Uint8Array) to improve the memory usage. If PDF data is + * BASE64-encoded, use `atob()` to convert it to a binary string first. + * + * NOTE: If TypedArrays are used they will generally be transferred to the + * worker-thread. This will help reduce main-thread memory usage, however + * it will take ownership of the TypedArrays. + * @property {Object} [httpHeaders] - Basic authentication headers. + * @property {boolean} [withCredentials] - Indicates whether or not + * cross-site Access-Control requests should be made using credentials such + * as cookies or authorization headers. The default is `false`. + * @property {string} [password] - For decrypting password-protected PDFs. + * @property {number} [length] - The PDF file length. It's used for progress + * reports and range requests operations. + * @property {PDFDataRangeTransport} [range] - Allows for using a custom range + * transport implementation. + * @property {number} [rangeChunkSize] - Specify maximum number of bytes fetched + * per range request. The default value is {@link DEFAULT_RANGE_CHUNK_SIZE}. + * @property {PDFWorker} [worker] - The worker that will be used for loading and + * parsing the PDF data. + * @property {number} [verbosity] - Controls the logging level; the constants + * from {@link VerbosityLevel} should be used. + * @property {string} [docBaseUrl] - The base URL of the document, used when + * attempting to recover valid absolute URLs for annotations, and outline + * items, that (incorrectly) only specify relative URLs. + * @property {string} [cMapUrl] - The URL where the predefined Adobe CMaps are + * located. Include the trailing slash. + * @property {boolean} [cMapPacked] - Specifies if the Adobe CMaps are binary + * packed or not. The default value is `true`. + * @property {Object} [CMapReaderFactory] - The factory that will be used when + * reading built-in CMap files. Providing a custom factory is useful for + * environments without Fetch API or `XMLHttpRequest` support, such as + * Node.js. The default value is {DOMCMapReaderFactory}. + * @property {boolean} [useSystemFonts] - When `true`, fonts that aren't + * embedded in the PDF document will fallback to a system font. + * The default value is `true` in web environments and `false` in Node.js; + * unless `disableFontFace === true` in which case this defaults to `false` + * regardless of the environment (to prevent completely broken fonts). + * @property {string} [standardFontDataUrl] - The URL where the standard font + * files are located. Include the trailing slash. + * @property {Object} [StandardFontDataFactory] - The factory that will be used + * when reading the standard font files. Providing a custom factory is useful + * for environments without Fetch API or `XMLHttpRequest` support, such as + * Node.js. The default value is {DOMStandardFontDataFactory}. + * @property {boolean} [useWorkerFetch] - Enable using the Fetch API in the + * worker-thread when reading CMap and standard font files. When `true`, + * the `CMapReaderFactory` and `StandardFontDataFactory` options are ignored. + * The default value is `true` in web environments and `false` in Node.js. + * @property {boolean} [stopAtErrors] - Reject certain promises, e.g. + * `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated + * PDF data cannot be successfully parsed, instead of attempting to recover + * whatever possible of the data. The default value is `false`. + * @property {number} [maxImageSize] - The maximum allowed image size in total + * pixels, i.e. width * height. Images above this value will not be rendered. + * Use -1 for no limit, which is also the default value. + * @property {boolean} [isEvalSupported] - Determines if we can evaluate strings + * as JavaScript. Primarily used to improve performance of PDF functions. + * The default value is `true`. + * @property {boolean} [isOffscreenCanvasSupported] - Determines if we can use + * `OffscreenCanvas` in the worker. Primarily used to improve performance of + * image conversion/rendering. + * The default value is `true` in web environments and `false` in Node.js. + * @property {number} [canvasMaxAreaInBytes] - The integer value is used to + * know when an image must be resized (uses `OffscreenCanvas` in the worker). + * If it's -1 then a possibly slow algorithm is used to guess the max value. + * @property {boolean} [disableFontFace] - By default fonts are converted to + * OpenType fonts and loaded via the Font Loading API or `@font-face` rules. + * If disabled, fonts will be rendered using a built-in font renderer that + * constructs the glyphs with primitive path commands. + * The default value is `false` in web environments and `true` in Node.js. + * @property {boolean} [fontExtraProperties] - Include additional properties, + * which are unused during rendering of PDF documents, when declareing the + * parsed font data from the worker-thread. This may be useful for debugging + * purposes (and backwards compatibility), but note that it will lead to + * increased memory usage. The default value is `false`. + * @property {boolean} [enableXfa] - Render Xfa forms if any. + * The default value is `false`. + * @property {HTMLDocument} [ownerDocument] - Specify an explicit document + * context to create elements with and to load resources, such as fonts, + * into. Defaults to the current document. + * @property {boolean} [disableRange] - Disable range request loading of PDF + * files. When enabled, and if the server supports partial content requests, + * then the PDF will be fetched in chunks. The default value is `false`. + * @property {boolean} [disableStream] - Disable streaming of PDF file data. + * By default PDF.js attempts to load PDF files in chunks. The default value + * is `false`. + * @property {boolean} [disableAutoFetch] - Disable pre-fetching of PDF file + * data. When range requests are enabled PDF.js will automatically keep + * fetching more data even if it isn't needed to display the current page. + * The default value is `false`. + * + * NOTE: It is also necessary to disable streaming, see above, in order for + * disabling of pre-fetching to work correctly. + * @property {boolean} [pdfBug] - Enables special hooks for debugging PDF.js + * (see `web/debugger.js`). The default value is `false`. + * @property {Object} [CanvasFactory] - The factory that will be used when + * creating canvases. The default value is {DOMCanvasFactory}. + * @property {Object} [FilterFactory] - The factory that will be used to + * create SVG filters when rendering some images on the main canvas. + * The default value is {DOMFilterFactory}. + * @property {boolean} [enableHWA] - Enables hardware acceleration for + * rendering. The default value is `false`. + */ +/** + * This is the main entry point for loading a PDF and interacting with it. + * + * NOTE: If a URL is used to fetch the PDF data a standard Fetch API call (or + * XHR as fallback) is used, which means it must follow same origin rules, + * e.g. no cross-domain requests without CORS. + * + * @param {string | URL | TypedArray | ArrayBuffer | DocumentInitParameters} + * src - Can be a URL where a PDF file is located, a typed array (Uint8Array) + * already populated with data, or a parameter object. + * @returns {PDFDocumentLoadingTask} + */ +declare function getDocument(src?: string | URL | TypedArray | ArrayBuffer | DocumentInitParameters): PDFDocumentLoadingTask; +declare class LoopbackPort { + postMessage(obj: any, transfer: any): void; + addEventListener(name: any, listener: any): void; + removeEventListener(name: any, listener: any): void; + terminate(): void; +} +/** + * Abstract class to support range requests file loading. + * + * NOTE: The TypedArrays passed to the constructor and relevant methods below + * will generally be transferred to the worker-thread. This will help reduce + * main-thread memory usage, however it will take ownership of the TypedArrays. + */ +declare class PDFDataRangeTransport { + /** + * @param {number} length + * @param {Uint8Array|null} initialData + * @param {boolean} [progressiveDone] + * @param {string} [contentDispositionFilename] + */ + constructor(length: number, initialData: Uint8Array | null, progressiveDone?: boolean | undefined, contentDispositionFilename?: string | undefined); + length: number; + initialData: Uint8Array | null; + progressiveDone: boolean; + contentDispositionFilename: string; + _rangeListeners: any[]; + _progressListeners: any[]; + _progressiveReadListeners: any[]; + _progressiveDoneListeners: any[]; + _readyCapability: any; + /** + * @param {function} listener + */ + addRangeListener(listener: Function): void; + /** + * @param {function} listener + */ + addProgressListener(listener: Function): void; + /** + * @param {function} listener + */ + addProgressiveReadListener(listener: Function): void; + /** + * @param {function} listener + */ + addProgressiveDoneListener(listener: Function): void; + /** + * @param {number} begin + * @param {Uint8Array|null} chunk + */ + onDataRange(begin: number, chunk: Uint8Array | null): void; + /** + * @param {number} loaded + * @param {number|undefined} total + */ + onDataProgress(loaded: number, total: number | undefined): void; + /** + * @param {Uint8Array|null} chunk + */ + onDataProgressiveRead(chunk: Uint8Array | null): void; + onDataProgressiveDone(): void; + transportReady(): void; + /** + * @param {number} begin + * @param {number} end + */ + requestDataRange(begin: number, end: number): void; + abort(): void; +} +/** + * @typedef {Object} OnProgressParameters + * @property {number} loaded - Currently loaded number of bytes. + * @property {number} total - Total number of bytes in the PDF file. + */ +/** + * The loading task controls the operations required to load a PDF document + * (such as network requests) and provides a way to listen for completion, + * after which individual pages can be rendered. + */ +declare class PDFDocumentLoadingTask { + static "__#47@#docId": number; + _capability: any; + _transport: any; + _worker: any; + /** + * Unique identifier for the document loading task. + * @type {string} + */ + docId: string; + /** + * Whether the loading task is destroyed or not. + * @type {boolean} + */ + destroyed: boolean; + /** + * Callback to request a password if a wrong or no password was provided. + * The callback receives two parameters: a function that should be called + * with the new password, and a reason (see {@link PasswordResponses}). + * @type {function} + */ + onPassword: Function; + /** + * Callback to be able to monitor the loading progress of the PDF file + * (necessary to implement e.g. a loading bar). + * The callback receives an {@link OnProgressParameters} argument. + * @type {function} + */ + onProgress: Function; + /** + * Promise for document loading task completion. + * @type {Promise<PDFDocumentProxy>} + */ + get promise(): Promise<PDFDocumentProxy>; + /** + * Abort all network requests and destroy the worker. + * @returns {Promise<void>} A promise that is resolved when destruction is + * completed. + */ + destroy(): Promise<void>; +} +/** + * Proxy to a `PDFDocument` in the worker thread. + */ +declare class PDFDocumentProxy { + constructor(pdfInfo: any, transport: any); + _pdfInfo: any; + _transport: any; + /** + * @type {AnnotationStorage} Storage for annotation data in forms. + */ + get annotationStorage(): AnnotationStorage; + /** + * @type {Object} The canvas factory instance. + */ + get canvasFactory(): Object; + /** + * @type {Object} The filter factory instance. + */ + get filterFactory(): Object; + /** + * @type {number} Total number of pages in the PDF file. + */ + get numPages(): number; + /** + * @type {Array<string, string|null>} A (not guaranteed to be) unique ID to + * identify the PDF document. + * NOTE: The first element will always be defined for all PDF documents, + * whereas the second element is only defined for *modified* PDF documents. + */ + get fingerprints(): string[]; + /** + * @type {boolean} True if only XFA form. + */ + get isPureXfa(): boolean; + /** + * NOTE: This is (mostly) intended to support printing of XFA forms. + * + * @type {Object | null} An object representing a HTML tree structure + * to render the XFA, or `null` when no XFA form exists. + */ + get allXfaHtml(): Object | null; + /** + * @param {number} pageNumber - The page number to get. The first page is 1. + * @returns {Promise<PDFPageProxy>} A promise that is resolved with + * a {@link PDFPageProxy} object. + */ + getPage(pageNumber: number): Promise<PDFPageProxy>; + /** + * @param {RefProxy} ref - The page reference. + * @returns {Promise<number>} A promise that is resolved with the page index, + * starting from zero, that is associated with the reference. + */ + getPageIndex(ref: RefProxy): Promise<number>; + /** + * @returns {Promise<Object<string, Array<any>>>} A promise that is resolved + * with a mapping from named destinations to references. + * + * This can be slow for large documents. Use `getDestination` instead. + */ + getDestinations(): Promise<{ + [x: string]: Array<any>; + }>; + /** + * @param {string} id - The named destination to get. + * @returns {Promise<Array<any> | null>} A promise that is resolved with all + * information of the given named destination, or `null` when the named + * destination is not present in the PDF file. + */ + getDestination(id: string): Promise<Array<any> | null>; + /** + * @returns {Promise<Array<string> | null>} A promise that is resolved with + * an {Array} containing the page labels that correspond to the page + * indexes, or `null` when no page labels are present in the PDF file. + */ + getPageLabels(): Promise<Array<string> | null>; + /** + * @returns {Promise<string>} A promise that is resolved with a {string} + * containing the page layout name. + */ + getPageLayout(): Promise<string>; + /** + * @returns {Promise<string>} A promise that is resolved with a {string} + * containing the page mode name. + */ + getPageMode(): Promise<string>; + /** + * @returns {Promise<Object | null>} A promise that is resolved with an + * {Object} containing the viewer preferences, or `null` when no viewer + * preferences are present in the PDF file. + */ + getViewerPreferences(): Promise<Object | null>; + /** + * @returns {Promise<any | null>} A promise that is resolved with an {Array} + * containing the destination, or `null` when no open action is present + * in the PDF. + */ + getOpenAction(): Promise<any | null>; + /** + * @returns {Promise<any>} A promise that is resolved with a lookup table + * for mapping named attachments to their content. + */ + getAttachments(): Promise<any>; + /** + * @returns {Promise<Object | null>} A promise that is resolved with + * an {Object} with the JavaScript actions: + * - from the name tree. + * - from A or AA entries in the catalog dictionary. + * , or `null` if no JavaScript exists. + */ + getJSActions(): Promise<Object | null>; + /** + * @typedef {Object} OutlineNode + * @property {string} title + * @property {boolean} bold + * @property {boolean} italic + * @property {Uint8ClampedArray} color - The color in RGB format to use for + * display purposes. + * @property {string | Array<any> | null} dest + * @property {string | null} url + * @property {string | undefined} unsafeUrl + * @property {boolean | undefined} newWindow + * @property {number | undefined} count + * @property {Array<OutlineNode>} items + */ + /** + * @returns {Promise<Array<OutlineNode>>} A promise that is resolved with an + * {Array} that is a tree outline (if it has one) of the PDF file. + */ + getOutline(): Promise<Array<{ + title: string; + bold: boolean; + italic: boolean; + /** + * - The color in RGB format to use for + * display purposes. + */ + color: Uint8ClampedArray; + dest: string | Array<any> | null; + url: string | null; + unsafeUrl: string | undefined; + newWindow: boolean | undefined; + count: number | undefined; + items: Array<any>; + }>>; + /** + * @typedef {Object} GetOptionalContentConfigParameters + * @property {string} [intent] - Determines the optional content groups that + * are visible by default; valid values are: + * - 'display' (viewable groups). + * - 'print' (printable groups). + * - 'any' (all groups). + * The default value is 'display'. + */ + /** + * @param {GetOptionalContentConfigParameters} [params] - Optional content + * config parameters. + * @returns {Promise<OptionalContentConfig>} A promise that is resolved with + * an {@link OptionalContentConfig} that contains all the optional content + * groups (assuming that the document has any). + */ + getOptionalContentConfig({ intent }?: { + /** + * - Determines the optional content groups that + * are visible by default; valid values are: + * - 'display' (viewable groups). + * - 'print' (printable groups). + * - 'any' (all groups). + * The default value is 'display'. + */ + intent?: string | undefined; + } | undefined): Promise<OptionalContentConfig>; + /** + * @returns {Promise<Array<number> | null>} A promise that is resolved with + * an {Array} that contains the permission flags for the PDF document, or + * `null` when no permissions are present in the PDF file. + */ + getPermissions(): Promise<Array<number> | null>; + /** + * @returns {Promise<{ info: Object, metadata: Metadata }>} A promise that is + * resolved with an {Object} that has `info` and `metadata` properties. + * `info` is an {Object} filled with anything available in the information + * dictionary and similarly `metadata` is a {Metadata} object with + * information from the metadata section of the PDF. + */ + getMetadata(): Promise<{ + info: Object; + metadata: Metadata; + }>; + /** + * @typedef {Object} MarkInfo + * Properties correspond to Table 321 of the PDF 32000-1:2008 spec. + * @property {boolean} Marked + * @property {boolean} UserProperties + * @property {boolean} Suspects + */ + /** + * @returns {Promise<MarkInfo | null>} A promise that is resolved with + * a {MarkInfo} object that contains the MarkInfo flags for the PDF + * document, or `null` when no MarkInfo values are present in the PDF file. + */ + getMarkInfo(): Promise<{ + Marked: boolean; + UserProperties: boolean; + Suspects: boolean; + } | null>; + /** + * @returns {Promise<Uint8Array>} A promise that is resolved with a + * {Uint8Array} containing the raw data of the PDF document. + */ + getData(): Promise<Uint8Array>; + /** + * @returns {Promise<Uint8Array>} A promise that is resolved with a + * {Uint8Array} containing the full data of the saved document. + */ + saveDocument(): Promise<Uint8Array>; + /** + * @returns {Promise<{ length: number }>} A promise that is resolved when the + * document's data is loaded. It is resolved with an {Object} that contains + * the `length` property that indicates size of the PDF data in bytes. + */ + getDownloadInfo(): Promise<{ + length: number; + }>; + /** + * Cleans up resources allocated by the document on both the main and worker + * threads. + * + * NOTE: Do not, under any circumstances, call this method when rendering is + * currently ongoing since that may lead to rendering errors. + * + * @param {boolean} [keepLoadedFonts] - Let fonts remain attached to the DOM. + * NOTE: This will increase persistent memory usage, hence don't use this + * option unless absolutely necessary. The default value is `false`. + * @returns {Promise} A promise that is resolved when clean-up has finished. + */ + cleanup(keepLoadedFonts?: boolean | undefined): Promise<any>; + /** + * Destroys the current document instance and terminates the worker. + */ + destroy(): Promise<void>; + /** + * @param {RefProxy} ref - The page reference. + * @returns {number | null} The page number, if it's cached. + */ + cachedPageNumber(ref: RefProxy): number | null; + /** + * @type {DocumentInitParameters} A subset of the current + * {DocumentInitParameters}, which are needed in the viewer. + */ + get loadingParams(): DocumentInitParameters; + /** + * @type {PDFDocumentLoadingTask} The loadingTask for the current document. + */ + get loadingTask(): PDFDocumentLoadingTask; + /** + * @returns {Promise<Object<string, Array<Object>> | null>} A promise that is + * resolved with an {Object} containing /AcroForm field data for the JS + * sandbox, or `null` when no field data is present in the PDF file. + */ + getFieldObjects(): Promise<{ + [x: string]: Array<Object>; + } | null>; + /** + * @returns {Promise<boolean>} A promise that is resolved with `true` + * if some /AcroForm fields have JavaScript actions. + */ + hasJSActions(): Promise<boolean>; + /** + * @returns {Promise<Array<string> | null>} A promise that is resolved with an + * {Array<string>} containing IDs of annotations that have a calculation + * action, or `null` when no such annotations are present in the PDF file. + */ + getCalculationOrderIds(): Promise<Array<string> | null>; +} +/** + * Page getViewport parameters. + * + * @typedef {Object} GetViewportParameters + * @property {number} scale - The desired scale of the viewport. + * @property {number} [rotation] - The desired rotation, in degrees, of + * the viewport. If omitted it defaults to the page rotation. + * @property {number} [offsetX] - The horizontal, i.e. x-axis, offset. + * The default value is `0`. + * @property {number} [offsetY] - The vertical, i.e. y-axis, offset. + * The default value is `0`. + * @property {boolean} [dontFlip] - If true, the y-axis will not be + * flipped. The default value is `false`. + */ +/** + * Page getTextContent parameters. + * + * @typedef {Object} getTextContentParameters + * @property {boolean} [includeMarkedContent] - When true include marked + * content items in the items array of TextContent. The default is `false`. + * @property {boolean} [disableNormalization] - When true the text is *not* + * normalized in the worker-thread. The default is `false`. + */ +/** + * Page text content. + * + * @typedef {Object} TextContent + * @property {Array<TextItem | TextMarkedContent>} items - Array of + * {@link TextItem} and {@link TextMarkedContent} objects. TextMarkedContent + * items are included when includeMarkedContent is true. + * @property {Object<string, TextStyle>} styles - {@link TextStyle} objects, + * indexed by font name. + * @property {string | null} lang - The document /Lang attribute. + */ +/** + * Page text content part. + * + * @typedef {Object} TextItem + * @property {string} str - Text content. + * @property {string} dir - Text direction: 'ttb', 'ltr' or 'rtl'. + * @property {Array<any>} transform - Transformation matrix. + * @property {number} width - Width in device space. + * @property {number} height - Height in device space. + * @property {string} fontName - Font name used by PDF.js for converted font. + * @property {boolean} hasEOL - Indicating if the text content is followed by a + * line-break. + */ +/** + * Page text marked content part. + * + * @typedef {Object} TextMarkedContent + * @property {string} type - Either 'beginMarkedContent', + * 'beginMarkedContentProps', or 'endMarkedContent'. + * @property {string} id - The marked content identifier. Only used for type + * 'beginMarkedContentProps'. + */ +/** + * Text style. + * + * @typedef {Object} TextStyle + * @property {number} ascent - Font ascent. + * @property {number} descent - Font descent. + * @property {boolean} vertical - Whether or not the text is in vertical mode. + * @property {string} fontFamily - The possible font family. + */ +/** + * Page annotation parameters. + * + * @typedef {Object} GetAnnotationsParameters + * @property {string} [intent] - Determines the annotations that are fetched, + * can be 'display' (viewable annotations), 'print' (printable annotations), + * or 'any' (all annotations). The default value is 'display'. + */ +/** + * Page render parameters. + * + * @typedef {Object} RenderParameters + * @property {CanvasRenderingContext2D} canvasContext - A 2D context of a DOM + * Canvas object. + * @property {PageViewport} viewport - Rendering viewport obtained by calling + * the `PDFPageProxy.getViewport` method. + * @property {string} [intent] - Rendering intent, can be 'display', 'print', + * or 'any'. The default value is 'display'. + * @property {number} [annotationMode] Controls which annotations are rendered + * onto the canvas, for annotations with appearance-data; the values from + * {@link AnnotationMode} should be used. The following values are supported: + * - `AnnotationMode.DISABLE`, which disables all annotations. + * - `AnnotationMode.ENABLE`, which includes all possible annotations (thus + * it also depends on the `intent`-option, see above). + * - `AnnotationMode.ENABLE_FORMS`, which excludes annotations that contain + * interactive form elements (those will be rendered in the display layer). + * - `AnnotationMode.ENABLE_STORAGE`, which includes all possible annotations + * (as above) but where interactive form elements are updated with data + * from the {@link AnnotationStorage}-instance; useful e.g. for printing. + * The default value is `AnnotationMode.ENABLE`. + * @property {Array<any>} [transform] - Additional transform, applied just + * before viewport transform. + * @property {CanvasGradient | CanvasPattern | string} [background] - Background + * to use for the canvas. + * Any valid `canvas.fillStyle` can be used: a `DOMString` parsed as CSS + * <color> value, a `CanvasGradient` object (a linear or radial gradient) or + * a `CanvasPattern` object (a repetitive image). The default value is + * 'rgb(255,255,255)'. + * + * NOTE: This option may be partially, or completely, ignored when the + * `pageColors`-option is used. + * @property {Object} [pageColors] - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] - + * A promise that should resolve with an {@link OptionalContentConfig} + * created from `PDFDocumentProxy.getOptionalContentConfig`. If `null`, + * the configuration will be fetched automatically with the default visibility + * states set. + * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] - Map some + * annotation ids with canvases used to render them. + * @property {PrintAnnotationStorage} [printAnnotationStorage] + * @property {boolean} [isEditing] - Render the page in editing mode. + */ +/** + * Page getOperatorList parameters. + * + * @typedef {Object} GetOperatorListParameters + * @property {string} [intent] - Rendering intent, can be 'display', 'print', + * or 'any'. The default value is 'display'. + * @property {number} [annotationMode] Controls which annotations are included + * in the operatorList, for annotations with appearance-data; the values from + * {@link AnnotationMode} should be used. The following values are supported: + * - `AnnotationMode.DISABLE`, which disables all annotations. + * - `AnnotationMode.ENABLE`, which includes all possible annotations (thus + * it also depends on the `intent`-option, see above). + * - `AnnotationMode.ENABLE_FORMS`, which excludes annotations that contain + * interactive form elements (those will be rendered in the display layer). + * - `AnnotationMode.ENABLE_STORAGE`, which includes all possible annotations + * (as above) but where interactive form elements are updated with data + * from the {@link AnnotationStorage}-instance; useful e.g. for printing. + * The default value is `AnnotationMode.ENABLE`. + * @property {PrintAnnotationStorage} [printAnnotationStorage] + * @property {boolean} [isEditing] - Render the page in editing mode. + */ +/** + * Structure tree node. The root node will have a role "Root". + * + * @typedef {Object} StructTreeNode + * @property {Array<StructTreeNode | StructTreeContent>} children - Array of + * {@link StructTreeNode} and {@link StructTreeContent} objects. + * @property {string} role - element's role, already mapped if a role map exists + * in the PDF. + */ +/** + * Structure tree content. + * + * @typedef {Object} StructTreeContent + * @property {string} type - either "content" for page and stream structure + * elements or "object" for object references. + * @property {string} id - unique id that will map to the text layer. + */ +/** + * PDF page operator list. + * + * @typedef {Object} PDFOperatorList + * @property {Array<number>} fnArray - Array containing the operator functions. + * @property {Array<any>} argsArray - Array containing the arguments of the + * functions. + */ +/** + * Proxy to a `PDFPage` in the worker thread. + */ +declare class PDFPageProxy { + constructor(pageIndex: any, pageInfo: any, transport: any, pdfBug?: boolean); + _pageIndex: any; + _pageInfo: any; + _transport: any; + _stats: StatTimer | null; + _pdfBug: boolean; + /** @type {PDFObjects} */ + commonObjs: PDFObjects; + objs: PDFObjects; + _maybeCleanupAfterRender: boolean; + _intentStates: Map<any, any>; + destroyed: boolean; + /** + * @type {number} Page number of the page. First page is 1. + */ + get pageNumber(): number; + /** + * @type {number} The number of degrees the page is rotated clockwise. + */ + get rotate(): number; + /** + * @type {RefProxy | null} The reference that points to this page. + */ + get ref(): RefProxy | null; + /** + * @type {number} The default size of units in 1/72nds of an inch. + */ + get userUnit(): number; + /** + * @type {Array<number>} An array of the visible portion of the PDF page in + * user space units [x1, y1, x2, y2]. + */ + get view(): number[]; + /** + * @param {GetViewportParameters} params - Viewport parameters. + * @returns {PageViewport} Contains 'width' and 'height' properties + * along with transforms required for rendering. + */ + getViewport({ scale, rotation, offsetX, offsetY, dontFlip, }?: GetViewportParameters): PageViewport; + /** + * @param {GetAnnotationsParameters} [params] - Annotation parameters. + * @returns {Promise<Array<any>>} A promise that is resolved with an + * {Array} of the annotation objects. + */ + getAnnotations({ intent }?: GetAnnotationsParameters | undefined): Promise<Array<any>>; + /** + * @returns {Promise<Object>} A promise that is resolved with an + * {Object} with JS actions. + */ + getJSActions(): Promise<Object>; + /** + * @type {Object} The filter factory instance. + */ + get filterFactory(): Object; + /** + * @type {boolean} True if only XFA form. + */ + get isPureXfa(): boolean; + /** + * @returns {Promise<Object | null>} A promise that is resolved with + * an {Object} with a fake DOM object (a tree structure where elements + * are {Object} with a name, attributes (class, style, ...), value and + * children, very similar to a HTML DOM tree), or `null` if no XFA exists. + */ + getXfa(): Promise<Object | null>; + /** + * Begins the process of rendering a page to the desired context. + * + * @param {RenderParameters} params - Page render parameters. + * @returns {RenderTask} An object that contains a promise that is + * resolved when the page finishes rendering. + */ + render({ canvasContext, viewport, intent, annotationMode, transform, background, optionalContentConfigPromise, annotationCanvasMap, pageColors, printAnnotationStorage, isEditing, }: RenderParameters): RenderTask; + /** + * @param {GetOperatorListParameters} params - Page getOperatorList + * parameters. + * @returns {Promise<PDFOperatorList>} A promise resolved with an + * {@link PDFOperatorList} object that represents the page's operator list. + */ + getOperatorList({ intent, annotationMode, printAnnotationStorage, isEditing, }?: GetOperatorListParameters): Promise<PDFOperatorList>; + /** + * NOTE: All occurrences of whitespace will be replaced by + * standard spaces (0x20). + * + * @param {getTextContentParameters} params - getTextContent parameters. + * @returns {ReadableStream} Stream for reading text content chunks. + */ + streamTextContent({ includeMarkedContent, disableNormalization, }?: getTextContentParameters): ReadableStream; + /** + * NOTE: All occurrences of whitespace will be replaced by + * standard spaces (0x20). + * + * @param {getTextContentParameters} params - getTextContent parameters. + * @returns {Promise<TextContent>} A promise that is resolved with a + * {@link TextContent} object that represents the page's text content. + */ + getTextContent(params?: getTextContentParameters): Promise<TextContent>; + /** + * @returns {Promise<StructTreeNode>} A promise that is resolved with a + * {@link StructTreeNode} object that represents the page's structure tree, + * or `null` when no structure tree is present for the current page. + */ + getStructTree(): Promise<StructTreeNode>; + /** + * Destroys the page object. + * @private + */ + private _destroy; + /** + * Cleans up resources allocated by the page. + * + * @param {boolean} [resetStats] - Reset page stats, if enabled. + * The default value is `false`. + * @returns {boolean} Indicates if clean-up was successfully run. + */ + cleanup(resetStats?: boolean | undefined): boolean; + /** + * @private + */ + private _startRenderPage; + /** + * @private + */ + private _renderPageChunk; + /** + * @private + */ + private _pumpOperatorList; + /** + * @private + */ + private _abortOperatorList; + /** + * @type {StatTimer | null} Returns page stats, if enabled; returns `null` + * otherwise. + */ + get stats(): StatTimer | null; +} +/** + * @typedef {Object} PDFWorkerParameters + * @property {string} [name] - The name of the worker. + * @property {Worker} [port] - The `workerPort` object. + * @property {number} [verbosity] - Controls the logging level; + * the constants from {@link VerbosityLevel} should be used. + */ +/** + * PDF.js web worker abstraction that controls the instantiation of PDF + * documents. Message handlers are used to pass information from the main + * thread to the worker thread and vice versa. If the creation of a web + * worker is not possible, a "fake" worker will be used instead. + * + * @param {PDFWorkerParameters} params - The worker initialization parameters. + */ +declare class PDFWorker { + static "__#50@#fakeWorkerId": number; + static "__#50@#isWorkerDisabled": boolean; + static "__#50@#workerPorts": any; + /** + * @param {PDFWorkerParameters} params - The worker initialization parameters. + */ + static fromPort(params: PDFWorkerParameters): any; + /** + * The current `workerSrc`, when it exists. + * @type {string} + */ + static get workerSrc(): string; + static get "__#50@#mainThreadWorkerMessageHandler"(): any; + static get _setupFakeWorkerGlobal(): any; + constructor({ name, port, verbosity, }?: { + name?: null | undefined; + port?: null | undefined; + verbosity?: number | undefined; + }); + name: any; + destroyed: boolean; + verbosity: number; + _readyCapability: any; + _port: any; + _webWorker: Worker | null; + _messageHandler: MessageHandler | null; + /** + * Promise for worker initialization completion. + * @type {Promise<void>} + */ + get promise(): Promise<void>; + /** + * The current `workerPort`, when it exists. + * @type {Worker} + */ + get port(): Worker; + /** + * The current MessageHandler-instance. + * @type {MessageHandler} + */ + get messageHandler(): MessageHandler; + _initializeFromPort(port: any): void; + _initialize(): void; + _setupFakeWorker(): void; + /** + * Destroys the worker instance. + */ + destroy(): void; +} +/** + * Allows controlling of the rendering tasks. + */ +declare class RenderTask { + constructor(internalRenderTask: any); + /** + * Callback for incremental rendering -- a function that will be called + * each time the rendering is paused. To continue rendering call the + * function that is the first argument to the callback. + * @type {function} + */ + onContinue: Function; + /** + * Promise for rendering task completion. + * @type {Promise<void>} + */ + get promise(): Promise<void>; + /** + * Cancels the rendering task. If the task is currently rendering it will + * not be cancelled until graphics pauses with a timeout. The promise that + * this object extends will be rejected when cancelled. + * + * @param {number} [extraDelay] + */ + cancel(extraDelay?: number | undefined): void; + /** + * Whether form fields are rendered separately from the main operatorList. + * @type {boolean} + */ + get separateAnnots(): boolean; +} +/** @type {string} */ +declare const version: string; +/** + * A PDF document and page is built of many objects. E.g. there are objects for + * fonts, images, rendering code, etc. These objects may get processed inside of + * a worker. This class implements some basic methods to manage these objects. + */ +declare class PDFObjects { + /** + * If called *without* callback, this returns the data of `objId` but the + * object needs to be resolved. If it isn't, this method throws. + * + * If called *with* a callback, the callback is called with the data of the + * object once the object is resolved. That means, if you call this method + * and the object is already resolved, the callback gets called right away. + * + * @param {string} objId + * @param {function} [callback] + * @returns {any} + */ + get(objId: string, callback?: Function | undefined): any; + /** + * @param {string} objId + * @returns {boolean} + */ + has(objId: string): boolean; + /** + * Resolves the object `objId` with optional `data`. + * + * @param {string} objId + * @param {any} [data] + */ + resolve(objId: string, data?: any): void; + clear(): void; + [Symbol.iterator](): Generator<any[], void, unknown>; +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/base_factory.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/base_factory.d.ts new file mode 100644 index 0000000000..135154dba5 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/base_factory.d.ts @@ -0,0 +1,59 @@ +declare class BaseCanvasFactory { + constructor({ enableHWA }: { + enableHWA?: boolean | undefined; + }); + create(width: any, height: any): { + canvas: void; + context: any; + }; + reset(canvasAndContext: any, width: any, height: any): void; + destroy(canvasAndContext: any): void; + /** + * @ignore + */ + _createCanvas(width: any, height: any): void; +} +declare class BaseCMapReaderFactory { + constructor({ baseUrl, isCompressed }: { + baseUrl?: null | undefined; + isCompressed?: boolean | undefined; + }); + baseUrl: any; + isCompressed: boolean; + fetch({ name }: { + name: any; + }): Promise<any>; + /** + * @ignore + */ + _fetchData(url: any, compressionType: any): void; +} +declare class BaseFilterFactory { + addFilter(maps: any): string; + addHCMFilter(fgColor: any, bgColor: any): string; + addAlphaFilter(map: any): string; + addLuminosityFilter(map: any): string; + addHighlightHCMFilter(filterName: any, fgColor: any, bgColor: any, newFgColor: any, newBgColor: any): string; + destroy(keepHCM?: boolean): void; +} +declare class BaseStandardFontDataFactory { + constructor({ baseUrl }: { + baseUrl?: null | undefined; + }); + baseUrl: any; + fetch({ filename }: { + filename: any; + }): Promise<any>; + /** + * @ignore + */ + _fetchData(url: any): void; +} +declare class BaseSVGFactory { + create(width: any, height: any, skipDimensions?: boolean): void; + createElement(type: any): void; + /** + * @ignore + */ + _createSVG(type: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/canvas.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/canvas.d.ts new file mode 100644 index 0000000000..2109677290 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/canvas.d.ts @@ -0,0 +1,210 @@ +declare class CanvasGraphics { + constructor(canvasCtx: any, commonObjs: any, objs: any, canvasFactory: any, filterFactory: any, { optionalContentConfig, markedContentStack }: { + optionalContentConfig: any; + markedContentStack?: null | undefined; + }, annotationCanvasMap: any, pageColors: any); + ctx: any; + current: CanvasExtraState; + stateStack: any[]; + pendingClip: {} | {} | null; + pendingEOFill: boolean; + res: any; + xobjs: any; + commonObjs: any; + objs: any; + canvasFactory: any; + filterFactory: any; + groupStack: any[]; + processingType3: any; + baseTransform: any; + baseTransformStack: any[]; + groupLevel: number; + smaskStack: any[]; + smaskCounter: number; + tempSMask: any; + suspendedCtx: any; + contentVisible: boolean; + markedContentStack: never[]; + optionalContentConfig: any; + cachedCanvases: CachedCanvases; + cachedPatterns: Map<any, any>; + annotationCanvasMap: any; + viewportScale: number; + outputScaleX: number; + outputScaleY: number; + pageColors: any; + _cachedScaleForStroking: number[]; + _cachedGetSinglePixelWidth: number | null; + _cachedBitmapsMap: Map<any, any>; + getObject(data: any, fallback?: null): any; + beginDrawing({ transform, viewport, transparency, background, }: { + transform: any; + viewport: any; + transparency?: boolean | undefined; + background?: null | undefined; + }): void; + compositeCtx: any; + transparentCanvas: any; + executeOperatorList(operatorList: any, executionStartIdx: any, continueCallback: any, stepper: any): any; + endDrawing(): void; + _scaleImage(img: any, inverseTransform: any): { + img: any; + paintWidth: any; + paintHeight: any; + }; + _createMaskCanvas(img: any): { + canvas: any; + offsetX: number; + offsetY: number; + }; + setLineWidth(width: any): void; + setLineCap(style: any): void; + setLineJoin(style: any): void; + setMiterLimit(limit: any): void; + setDash(dashArray: any, dashPhase: any): void; + setRenderingIntent(intent: any): void; + setFlatness(flatness: any): void; + setGState(states: any): void; + get inSMaskMode(): boolean; + checkSMaskState(): void; + /** + * Soft mask mode takes the current main drawing canvas and replaces it with + * a temporary canvas. Any drawing operations that happen on the temporary + * canvas need to be composed with the main canvas that was suspended (see + * `compose()`). The temporary canvas also duplicates many of its operations + * on the suspended canvas to keep them in sync, so that when the soft mask + * mode ends any clipping paths or transformations will still be active and in + * the right order on the canvas' graphics state stack. + */ + beginSMaskMode(): void; + endSMaskMode(): void; + compose(dirtyBox: any): void; + composeSMask(ctx: any, smask: any, layerCtx: any, layerBox: any): void; + genericComposeSMask(maskCtx: any, layerCtx: any, width: any, height: any, subtype: any, backdrop: any, transferMap: any, layerOffsetX: any, layerOffsetY: any, maskOffsetX: any, maskOffsetY: any): void; + save(): void; + restore(): void; + transform(a: any, b: any, c: any, d: any, e: any, f: any): void; + constructPath(ops: any, args: any, minMax: any): void; + closePath(): void; + stroke(consumePath?: boolean): void; + closeStroke(): void; + fill(consumePath?: boolean): void; + eoFill(): void; + fillStroke(): void; + eoFillStroke(): void; + closeFillStroke(): void; + closeEOFillStroke(): void; + endPath(): void; + clip(): void; + eoClip(): void; + beginText(): void; + endText(): void; + setCharSpacing(spacing: any): void; + setWordSpacing(spacing: any): void; + setHScale(scale: any): void; + setLeading(leading: any): void; + setFont(fontRefName: any, size: any): void; + setTextRenderingMode(mode: any): void; + setTextRise(rise: any): void; + moveText(x: any, y: any): void; + setLeadingMoveText(x: any, y: any): void; + setTextMatrix(a: any, b: any, c: any, d: any, e: any, f: any): void; + nextLine(): void; + paintChar(character: any, x: any, y: any, patternTransform: any): void; + get isFontSubpixelAAEnabled(): any; + showText(glyphs: any): void; + showType3Text(glyphs: any): void; + setCharWidth(xWidth: any, yWidth: any): void; + setCharWidthAndBounds(xWidth: any, yWidth: any, llx: any, lly: any, urx: any, ury: any): void; + getColorN_Pattern(IR: any): any; + setStrokeColorN(...args: any[]): void; + setFillColorN(...args: any[]): void; + setStrokeRGBColor(r: any, g: any, b: any): void; + setStrokeTransparent(): void; + setFillRGBColor(r: any, g: any, b: any): void; + setFillTransparent(): void; + _getPattern(objId: any, matrix?: null): any; + shadingFill(objId: any): void; + beginInlineImage(): void; + beginImageData(): void; + paintFormXObjectBegin(matrix: any, bbox: any): void; + paintFormXObjectEnd(): void; + beginGroup(group: any): void; + endGroup(group: any): void; + beginAnnotation(id: any, rect: any, transform: any, matrix: any, hasOwnCanvas: any): void; + annotationCanvas: any; + endAnnotation(): void; + paintImageMaskXObject(img: any): void; + paintImageMaskXObjectRepeat(img: any, scaleX: any, skewX: number | undefined, skewY: number | undefined, scaleY: any, positions: any): void; + paintImageMaskXObjectGroup(images: any): void; + paintImageXObject(objId: any): void; + paintImageXObjectRepeat(objId: any, scaleX: any, scaleY: any, positions: any): void; + applyTransferMapsToCanvas(ctx: any): any; + applyTransferMapsToBitmap(imgData: any): any; + paintInlineImageXObject(imgData: any): void; + paintInlineImageXObjectGroup(imgData: any, map: any): void; + paintSolidColorImageMask(): void; + markPoint(tag: any): void; + markPointProps(tag: any, properties: any): void; + beginMarkedContent(tag: any): void; + beginMarkedContentProps(tag: any, properties: any): void; + endMarkedContent(): void; + beginCompat(): void; + endCompat(): void; + consumePath(clipBox: any): void; + getSinglePixelWidth(): number; + getScaleForStroking(): number[]; + rescaleAndStroke(saveRestore: any): void; + isContentVisible(): boolean; +} +declare class CanvasExtraState { + constructor(width: any, height: any); + alphaIsShape: boolean; + fontSize: number; + fontSizeScale: number; + textMatrix: number[]; + textMatrixScale: number; + fontMatrix: number[]; + leading: number; + x: number; + y: number; + lineX: number; + lineY: number; + charSpacing: number; + wordSpacing: number; + textHScale: number; + textRenderingMode: number; + textRise: number; + fillColor: string; + strokeColor: string; + patternFill: boolean; + fillAlpha: number; + strokeAlpha: number; + lineWidth: number; + activeSMask: any; + transferMaps: string; + clone(): any; + setCurrentPoint(x: any, y: any): void; + updatePathMinMax(transform: any, x: any, y: any): void; + minX: any; + minY: any; + maxX: any; + maxY: any; + updateRectMinMax(transform: any, rect: any): void; + updateScalingPathMinMax(transform: any, minMax: any): void; + updateCurvePathMinMax(transform: any, x0: any, y0: any, x1: any, y1: any, x2: any, y2: any, x3: any, y3: any, minMax: any): void; + getPathBoundingBox(pathType?: string, transform?: null): any[]; + updateClipFromPath(): void; + isEmptyClip(): boolean; + startNewPathAndClipBox(box: any): void; + clipBox: any; + getClippedPathBoundingBox(pathType?: string, transform?: null): number[] | null; +} +declare class CachedCanvases { + constructor(canvasFactory: any); + canvasFactory: any; + cache: any; + getCanvas(id: any, width: any, height: any): any; + delete(id: any): void; + clear(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/content_disposition.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/content_disposition.d.ts new file mode 100644 index 0000000000..ece5f51e6a --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/content_disposition.d.ts @@ -0,0 +1,7 @@ +/** + * Extract file name from the Content-Disposition HTTP response header. + * + * @param {string} contentDisposition + * @returns {string} Filename, if found in the Content-Disposition header. + */ +declare function getFilenameFromContentDispositionHeader(contentDisposition: string): string; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/display_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/display_utils.d.ts new file mode 100644 index 0000000000..748a09e95f --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/display_utils.d.ts @@ -0,0 +1,288 @@ +declare type PageViewportParameters = { + /** + * - The xMin, yMin, xMax and + * yMax coordinates. + */ + viewBox: Array<number>; + /** + * - The scale of the viewport. + */ + scale: number; + /** + * - The rotation, in degrees, of the viewport. + */ + rotation: number; + /** + * - The horizontal, i.e. x-axis, offset. The + * default value is `0`. + */ + offsetX?: number | undefined; + /** + * - The vertical, i.e. y-axis, offset. The + * default value is `0`. + */ + offsetY?: number | undefined; + /** + * - If true, the y-axis will not be flipped. + * The default value is `false`. + */ + dontFlip?: boolean | undefined; +}; +declare type PageViewportCloneParameters = { + /** + * - The scale, overriding the one in the cloned + * viewport. The default value is `this.scale`. + */ + scale?: number | undefined; + /** + * - The rotation, in degrees, overriding the one + * in the cloned viewport. The default value is `this.rotation`. + */ + rotation?: number | undefined; + /** + * - The horizontal, i.e. x-axis, offset. + * The default value is `this.offsetX`. + */ + offsetX?: number | undefined; + /** + * - The vertical, i.e. y-axis, offset. + * The default value is `this.offsetY`. + */ + offsetY?: number | undefined; + /** + * - If true, the x-axis will not be flipped. + * The default value is `false`. + */ + dontFlip?: boolean | undefined; +}; +declare function deprecated(details: any): void; +declare class DOMCanvasFactory extends BaseCanvasFactory { + constructor({ ownerDocument, enableHWA }: { + ownerDocument?: Document | undefined; + enableHWA?: boolean | undefined; + }); + _document: Document; + /** + * @ignore + */ + _createCanvas(width: any, height: any): HTMLCanvasElement; +} +declare class DOMCMapReaderFactory extends BaseCMapReaderFactory { + /** + * @ignore + */ + _fetchData(url: any, compressionType: any): Promise<{ + cMapData: Uint8Array; + compressionType: any; + }>; +} +/** + * FilterFactory aims to create some SVG filters we can use when drawing an + * image (or whatever) on a canvas. + * Filters aren't applied with ctx.putImageData because it just overwrites the + * underlying pixels. + * With these filters, it's possible for example to apply some transfer maps on + * an image without the need to apply them on the pixel arrays: the renderer + * does the magic for us. + */ +declare class DOMFilterFactory extends BaseFilterFactory { + constructor({ docId, ownerDocument }: { + docId: any; + ownerDocument?: Document | undefined; + }); + addFilter(maps: any): any; + addHCMFilter(fgColor: any, bgColor: any): any; + addAlphaFilter(map: any): any; + addLuminosityFilter(map: any): any; + addHighlightHCMFilter(filterName: any, fgColor: any, bgColor: any, newFgColor: any, newBgColor: any): any; +} +declare class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { + /** + * @ignore + */ + _fetchData(url: any): Promise<Uint8Array>; +} +declare class DOMSVGFactory extends BaseSVGFactory { + /** + * @ignore + */ + _createSVG(type: any): any; +} +declare function fetchData(url: any, type?: string): Promise<any>; +declare function getColorValues(colors: any): void; +declare function getCurrentTransform(ctx: any): any[]; +declare function getCurrentTransformInverse(ctx: any): any[]; +/** + * Gets the filename from a given URL. + * @param {string} url + * @returns {string} + */ +declare function getFilenameFromUrl(url: string): string; +/** + * Returns the filename or guessed filename from the url (see issue 3455). + * @param {string} url - The original PDF location. + * @param {string} defaultFilename - The value returned if the filename is + * unknown, or the protocol is unsupported. + * @returns {string} Guessed PDF filename. + */ +declare function getPdfFilenameFromUrl(url: string, defaultFilename?: string): string; +declare function getRGB(color: any): any; +/** + * NOTE: This is (mostly) intended to support printing of XFA forms. + */ +declare function getXfaPageViewport(xfaPage: any, { scale, rotation }: { + scale?: number | undefined; + rotation?: number | undefined; +}): PageViewport; +declare function isDataScheme(url: any): boolean; +declare function isPdfFile(filename: any): boolean; +declare function isValidFetchUrl(url: any, baseUrl: any): boolean; +/** + * Event handler to suppress context menu. + */ +declare function noContextMenu(e: any): void; +/** + * Scale factors for the canvas, necessary with HiDPI displays. + */ +declare class OutputScale { + /** + * @type {number} Horizontal scale. + */ + sx: number; + /** + * @type {number} Vertical scale. + */ + sy: number; + /** + * @type {boolean} Returns `true` when scaling is required, `false` otherwise. + */ + get scaled(): boolean; + get symmetric(): boolean; +} +/** + * @typedef {Object} PageViewportParameters + * @property {Array<number>} viewBox - The xMin, yMin, xMax and + * yMax coordinates. + * @property {number} scale - The scale of the viewport. + * @property {number} rotation - The rotation, in degrees, of the viewport. + * @property {number} [offsetX] - The horizontal, i.e. x-axis, offset. The + * default value is `0`. + * @property {number} [offsetY] - The vertical, i.e. y-axis, offset. The + * default value is `0`. + * @property {boolean} [dontFlip] - If true, the y-axis will not be flipped. + * The default value is `false`. + */ +/** + * @typedef {Object} PageViewportCloneParameters + * @property {number} [scale] - The scale, overriding the one in the cloned + * viewport. The default value is `this.scale`. + * @property {number} [rotation] - The rotation, in degrees, overriding the one + * in the cloned viewport. The default value is `this.rotation`. + * @property {number} [offsetX] - The horizontal, i.e. x-axis, offset. + * The default value is `this.offsetX`. + * @property {number} [offsetY] - The vertical, i.e. y-axis, offset. + * The default value is `this.offsetY`. + * @property {boolean} [dontFlip] - If true, the x-axis will not be flipped. + * The default value is `false`. + */ +/** + * PDF page viewport created based on scale, rotation and offset. + */ +declare class PageViewport { + /** + * @param {PageViewportParameters} + */ + constructor({ viewBox, scale, rotation, offsetX, offsetY, dontFlip, }: PageViewportParameters); + viewBox: number[]; + scale: number; + rotation: number; + offsetX: number; + offsetY: number; + transform: number[]; + width: number; + height: number; + /** + * The original, un-scaled, viewport dimensions. + * @type {Object} + */ + get rawDims(): Object; + /** + * Clones viewport, with optional additional properties. + * @param {PageViewportCloneParameters} [params] + * @returns {PageViewport} Cloned viewport. + */ + clone({ scale, rotation, offsetX, offsetY, dontFlip, }?: PageViewportCloneParameters | undefined): PageViewport; + /** + * Converts PDF point to the viewport coordinates. For examples, useful for + * converting PDF location into canvas pixel coordinates. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {Array} Array containing `x`- and `y`-coordinates of the + * point in the viewport coordinate space. + * @see {@link convertToPdfPoint} + * @see {@link convertToViewportRectangle} + */ + convertToViewportPoint(x: number, y: number): any[]; + /** + * Converts PDF rectangle to the viewport coordinates. + * @param {Array} rect - The xMin, yMin, xMax and yMax coordinates. + * @returns {Array} Array containing corresponding coordinates of the + * rectangle in the viewport coordinate space. + * @see {@link convertToViewportPoint} + */ + convertToViewportRectangle(rect: any[]): any[]; + /** + * Converts viewport coordinates to the PDF location. For examples, useful + * for converting canvas pixel location into PDF one. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {Array} Array containing `x`- and `y`-coordinates of the + * point in the PDF coordinate space. + * @see {@link convertToViewportPoint} + */ + convertToPdfPoint(x: number, y: number): any[]; +} +declare class PDFDateString { + /** + * Convert a PDF date string to a JavaScript `Date` object. + * + * The PDF date string format is described in section 7.9.4 of the official + * PDF 32000-1:2008 specification. However, in the PDF 1.7 reference (sixth + * edition) Adobe describes the same format including a trailing apostrophe. + * This syntax in incorrect, but Adobe Acrobat creates PDF files that contain + * them. We ignore all apostrophes as they are not necessary for date parsing. + * + * Moreover, Adobe Acrobat doesn't handle changing the date to universal time + * and doesn't use the user's time zone (effectively ignoring the HH' and mm' + * parts of the date string). + * + * @param {string} input + * @returns {Date|null} + */ + static toDateObject(input: string): Date | null; +} +declare class PixelsPerInch { + static CSS: number; + static PDF: number; + static PDF_TO_CSS_UNITS: number; +} +declare const RenderingCancelledException_base: any; +declare class RenderingCancelledException extends RenderingCancelledException_base { + [x: string]: any; + constructor(msg: any, extraDelay?: number); + extraDelay: number; +} +/** + * @param {HTMLDivElement} div + * @param {PageViewport} viewport + * @param {boolean} mustFlip + * @param {boolean} mustRotate + */ +declare function setLayerDimensions(div: HTMLDivElement, viewport: PageViewport, mustFlip?: boolean, mustRotate?: boolean): void; +declare class StatTimer { + started: any; + times: any[]; + time(name: any): void; + timeEnd(name: any): void; + toString(): string; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/draw_layer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/draw_layer.d.ts new file mode 100644 index 0000000000..75ed59ab25 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/draw_layer.d.ts @@ -0,0 +1,38 @@ +/** + * Manage the SVGs drawn on top of the page canvas. + * It's important to have them directly on top of the canvas because we want to + * be able to use mix-blend-mode for some of them. + */ +declare class DrawLayer { + static get _svgFactory(): any; + static "__#28@#setBox"(element: any, { x, y, width, height }?: { + x?: number | undefined; + y?: number | undefined; + width?: number | undefined; + height?: number | undefined; + }): void; + constructor({ pageIndex }: { + pageIndex: any; + }); + pageIndex: any; + setParent(parent: any): void; + highlight(outlines: any, color: any, opacity: any, isPathUpdatable?: boolean): { + id: number; + clipPathId: string; + }; + highlightOutline(outlines: any): number; + finalizeLine(id: any, line: any): void; + updateLine(id: any, line: any): void; + removeFreeHighlight(id: any): void; + updatePath(id: any, line: any): void; + updateBox(id: any, box: any): void; + show(id: any, visible: any): void; + rotate(id: any, angle: any): void; + changeColor(id: any, color: any): void; + changeOpacity(id: any, opacity: any): void; + addClass(id: any, className: any): void; + removeClass(id: any, className: any): void; + getSVGRoot(id: any): any; + remove(id: any): void; + destroy(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/alt_text.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/alt_text.d.ts new file mode 100644 index 0000000000..062277c4af --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/alt_text.d.ts @@ -0,0 +1,36 @@ +declare class AltText { + static "__#35@#l10nNewButton": null; + static _l10nPromise: null; + static initialize(l10nPromise: any): void; + constructor(editor: any); + render(): Promise<HTMLButtonElement>; + finish(): void; + isEmpty(): boolean; + hasData(): boolean; + get guessedText(): null; + setGuessedText(guessedText: any): Promise<void>; + toggleAltTextBadge(visibility?: boolean): void; + serialize(isForCopying: any): { + altText: null; + decorative: boolean; + guessedText: null; + textWithDisclaimer: null; + }; + /** + * Set the alt text data. + */ + set data({ altText, decorative, guessedText, textWithDisclaimer, cancel }: { + altText: null; + decorative: boolean; + guessedText: any; + textWithDisclaimer: any; + cancel: any; + }); + get data(): { + altText: null; + decorative: boolean; + }; + toggle(enabled?: boolean): void; + shown(): void; + destroy(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/annotation_editor_layer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/annotation_editor_layer.d.ts new file mode 100644 index 0000000000..dd4a2d9f5e --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/annotation_editor_layer.d.ts @@ -0,0 +1,216 @@ +declare type AnnotationEditorLayerOptions = { + mode: Object; + div: HTMLDivElement; + structTreeLayer: StructTreeLayerBuilder; + uiManager: AnnotationEditorUIManager; + enabled: boolean; + accessibilityManager?: TextAccessibilityManager | undefined; + pageIndex: number; + l10n: IL10n; + annotationLayer?: AnnotationLayer | undefined; + textLayer?: HTMLDivElement | undefined; + drawLayer: DrawLayer; + viewport: PageViewport; +}; +declare type RenderEditorLayerOptions = { + viewport: PageViewport; +}; +/** + * @typedef {Object} AnnotationEditorLayerOptions + * @property {Object} mode + * @property {HTMLDivElement} div + * @property {StructTreeLayerBuilder} structTreeLayer + * @property {AnnotationEditorUIManager} uiManager + * @property {boolean} enabled + * @property {TextAccessibilityManager} [accessibilityManager] + * @property {number} pageIndex + * @property {IL10n} l10n + * @property {AnnotationLayer} [annotationLayer] + * @property {HTMLDivElement} [textLayer] + * @property {DrawLayer} drawLayer + * @property {PageViewport} viewport + */ +/** + * @typedef {Object} RenderEditorLayerOptions + * @property {PageViewport} viewport + */ +/** + * Manage all the different editors on a page. + */ +declare class AnnotationEditorLayer { + static _initialized: boolean; + static "__#29@#editorTypes": Map<number, typeof FreeTextEditor | typeof HighlightEditor | typeof InkEditor | typeof StampEditor>; + /** + * @param {AnnotationEditorLayerOptions} options + */ + constructor({ uiManager, pageIndex, div, structTreeLayer, accessibilityManager, annotationLayer, drawLayer, textLayer, viewport, l10n, }: AnnotationEditorLayerOptions); + pageIndex: number; + div: HTMLDivElement; + viewport: PageViewport; + drawLayer: DrawLayer; + _structTree: any; + get isEmpty(): boolean; + get isInvisible(): boolean; + /** + * Update the toolbar if it's required to reflect the tool currently used. + * @param {number} mode + */ + updateToolbar(mode: number): void; + /** + * The mode has changed: it must be updated. + * @param {number} mode + */ + updateMode(mode?: number): void; + hasTextLayer(textLayer: any): boolean; + addInkEditorIfNeeded(isCommitting: any): void; + /** + * Set the editing state. + * @param {boolean} isEditing + */ + setEditingState(isEditing: boolean): void; + /** + * Add some commands into the CommandManager (undo/redo stuff). + * @param {Object} params + */ + addCommands(params: Object): void; + toggleDrawing(enabled?: boolean): void; + togglePointerEvents(enabled?: boolean): void; + toggleAnnotationLayerPointerEvents(enabled?: boolean): void; + /** + * Enable pointer events on the main div in order to enable + * editor creation. + */ + enable(): Promise<void>; + /** + * Disable editor creation. + */ + disable(): void; + getEditableAnnotation(id: any): any; + /** + * Set the current editor. + * @param {AnnotationEditor} editor + */ + setActiveEditor(editor: AnnotationEditor): void; + enableTextSelection(): void; + disableTextSelection(): void; + enableClick(): void; + disableClick(): void; + attach(editor: any): void; + detach(editor: any): void; + /** + * Remove an editor. + * @param {AnnotationEditor} editor + */ + remove(editor: AnnotationEditor): void; + /** + * An editor can have a different parent, for example after having + * being dragged and droped from a page to another. + * @param {AnnotationEditor} editor + */ + changeParent(editor: AnnotationEditor): void; + /** + * Add a new editor in the current view. + * @param {AnnotationEditor} editor + */ + add(editor: AnnotationEditor): void; + moveEditorInDOM(editor: any): void; + /** + * Add or rebuild depending if it has been removed or not. + * @param {AnnotationEditor} editor + */ + addOrRebuild(editor: AnnotationEditor): void; + /** + * Add a new editor and make this addition undoable. + * @param {AnnotationEditor} editor + */ + addUndoableEditor(editor: AnnotationEditor): void; + /** + * Get an id for an editor. + * @returns {string} + */ + getNextId(): string; + combinedSignal(ac: any): AbortSignal; + canCreateNewEmptyEditor(): boolean | undefined; + /** + * Paste some content into a new editor. + * @param {number} mode + * @param {Object} params + */ + pasteEditor(mode: number, params: Object): void; + /** + * Create a new editor + * @param {Object} data + * @returns {AnnotationEditor | null} + */ + deserialize(data: Object): AnnotationEditor | null; + /** + * Create and add a new editor. + * @param {PointerEvent} event + * @param {boolean} isCentered + * @param [Object] data + * @returns {AnnotationEditor} + */ + createAndAddNewEditor(event: PointerEvent, isCentered: boolean, data?: {}): AnnotationEditor; + /** + * Create and add a new editor. + */ + addNewEditor(): void; + /** + * Set the last selected editor. + * @param {AnnotationEditor} editor + */ + setSelected(editor: AnnotationEditor): void; + /** + * Add or remove an editor the current selection. + * @param {AnnotationEditor} editor + */ + toggleSelected(editor: AnnotationEditor): void; + /** + * Check if the editor is selected. + * @param {AnnotationEditor} editor + */ + isSelected(editor: AnnotationEditor): boolean; + /** + * Unselect an editor. + * @param {AnnotationEditor} editor + */ + unselect(editor: AnnotationEditor): void; + /** + * Pointerup callback. + * @param {PointerEvent} event + */ + pointerup(event: PointerEvent): void; + /** + * Pointerdown callback. + * @param {PointerEvent} event + */ + pointerdown(event: PointerEvent): void; + /** + * + * @param {AnnotationEditor} editor + * @param {number} x + * @param {number} y + * @returns + */ + findNewParent(editor: AnnotationEditor, x: number, y: number): boolean; + /** + * Destroy the main editor. + */ + destroy(): void; + /** + * Render the main editor. + * @param {RenderEditorLayerOptions} parameters + */ + render({ viewport }: RenderEditorLayerOptions): void; + /** + * Update the main editor. + * @param {RenderEditorLayerOptions} parameters + */ + update({ viewport }: RenderEditorLayerOptions): void; + /** + * Get page dimensions. + * @returns {Object} dimensions. + */ + get pageDimensions(): Object; + get scale(): number; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/color_picker.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/color_picker.d.ts new file mode 100644 index 0000000000..a7111f18f8 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/color_picker.d.ts @@ -0,0 +1,19 @@ +declare class ColorPicker { + static "__#24@#l10nColor": null; + static get _keyboardManager(): any; + constructor({ editor, uiManager }: { + editor?: null | undefined; + uiManager?: null | undefined; + }); + renderButton(): HTMLButtonElement; + renderMainDropdown(): HTMLDivElement; + _colorSelectFromKeyboard(event: any): void; + _moveToNext(event: any): void; + _moveToPrevious(event: any): void; + _moveToBeginning(event: any): void; + _moveToEnd(event: any): void; + hideDropdown(): void; + _hideDropdownFromKeyboard(): void; + updateColor(color: any): void; + destroy(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/editor.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/editor.d.ts new file mode 100644 index 0000000000..c2366a42bf --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/editor.d.ts @@ -0,0 +1,434 @@ +declare type AnnotationEditorParameters = { + /** + * - the global manager + */ + uiManager: AnnotationEditorUIManager; + /** + * - the layer containing this editor + */ + parent: AnnotationEditorLayer; + /** + * - editor id + */ + id: string; + /** + * - x-coordinate + */ + x: number; + /** + * - y-coordinate + */ + y: number; +}; +/** + * @typedef {Object} AnnotationEditorParameters + * @property {AnnotationEditorUIManager} uiManager - the global manager + * @property {AnnotationEditorLayer} parent - the layer containing this editor + * @property {string} id - editor id + * @property {number} x - x-coordinate + * @property {number} y - y-coordinate + */ +/** + * Base class for editors. + */ +declare class AnnotationEditor { + static _l10nPromise: null; + static _l10nResizer: null; + static _borderLineWidth: number; + static _colorManager: ColorManager; + static _zIndex: number; + static _telemetryTimeout: number; + static get _resizerKeyboardManager(): any; + static get _defaultLineColor(): any; + static deleteAnnotationElement(editor: any): void; + /** + * Initialize the l10n stuff for this type of editor. + * @param {Object} l10n + */ + static initialize(l10n: Object, _uiManager: any, options: any): void; + /** + * Update the default parameters for this type of editor. + * @param {number} _type + * @param {*} _value + */ + static updateDefaultParams(_type: number, _value: any): void; + /** + * Get the default properties to set in the UI for this type of editor. + * @returns {Array} + */ + static get defaultPropertiesToUpdate(): any[]; + /** + * Check if this kind of editor is able to handle the given mime type for + * pasting. + * @param {string} mime + * @returns {boolean} + */ + static isHandlingMimeForPasting(mime: string): boolean; + /** + * Extract the data from the clipboard item and delegate the creation of the + * editor to the parent. + * @param {DataTransferItem} item + * @param {AnnotationEditorLayer} parent + */ + static paste(item: DataTransferItem, parent: AnnotationEditorLayer): void; + static "__#36@#rotatePoint"(x: any, y: any, angle: any): any[]; + /** + * Deserialize the editor. + * The result of the deserialization is a new editor. + * + * @param {Object} data + * @param {AnnotationEditorLayer} parent + * @param {AnnotationEditorUIManager} uiManager + * @returns {Promise<AnnotationEditor | null>} + */ + static deserialize(data: Object, parent: AnnotationEditorLayer, uiManager: AnnotationEditorUIManager): Promise<AnnotationEditor | null>; + static get MIN_SIZE(): number; + static canCreateNewEmptyEditor(): boolean; + /** + * @param {AnnotationEditorParameters} parameters + */ + constructor(parameters: AnnotationEditorParameters); + _editToolbar: null; + _initialOptions: any; + _initialData: null; + _isVisible: boolean; + _uiManager: null; + _focusEventsAllowed: boolean; + parent: AnnotationEditorLayer; + id: string; + width: any; + height: any; + pageIndex: number; + name: any; + div: HTMLDivElement | null; + annotationElementId: any; + _willKeepAspectRatio: boolean; + _structTreeParentId: any; + rotation: number; + pageRotation: number; + pageDimensions: any[]; + pageTranslation: any[]; + x: number; + y: number; + isAttachedToDOM: boolean; + deleted: boolean; + get editorType(): any; + /** + * Get the properties to update in the UI for this editor. + * @returns {Array} + */ + get propertiesToUpdate(): any[]; + set _isDraggable(value: boolean); + get _isDraggable(): boolean; + /** + * @returns {boolean} true if the editor handles the Enter key itself. + */ + get isEnterHandled(): boolean; + center(): void; + /** + * Add some commands into the CommandManager (undo/redo stuff). + * @param {Object} params + */ + addCommands(params: Object): void; + get currentLayer(): any; + /** + * This editor will be behind the others. + */ + setInBackground(): void; + /** + * This editor will be in the foreground. + */ + setInForeground(): void; + setParent(parent: any): void; + /** + * onfocus callback. + */ + focusin(event: any): void; + /** + * onblur callback. + * @param {FocusEvent} event + */ + focusout(event: FocusEvent): void; + commitOrRemove(): void; + /** + * Commit the data contained in this editor. + */ + commit(): void; + addToAnnotationStorage(): void; + /** + * Set the editor position within its parent. + * @param {number} x + * @param {number} y + * @param {number} tx - x-translation in screen coordinates. + * @param {number} ty - y-translation in screen coordinates. + */ + setAt(x: number, y: number, tx: number, ty: number): void; + /** + * Translate the editor position within its parent. + * @param {number} x - x-translation in screen coordinates. + * @param {number} y - y-translation in screen coordinates. + */ + translate(x: number, y: number): void; + /** + * Translate the editor position within its page and adjust the scroll + * in order to have the editor in the view. + * @param {number} x - x-translation in page coordinates. + * @param {number} y - y-translation in page coordinates. + */ + translateInPage(x: number, y: number): void; + drag(tx: any, ty: any): void; + get _hasBeenMoved(): boolean; + /** + * Get the translation to take into account the editor border. + * The CSS engine positions the element by taking the border into account so + * we must apply the opposite translation to have the editor in the right + * position. + * @returns {Array<number>} + */ + getBaseTranslation(): Array<number>; + /** + * @returns {boolean} true if position must be fixed (i.e. make the x and y + * living in the page). + */ + get _mustFixPosition(): boolean; + /** + * Fix the position of the editor in order to keep it inside its parent page. + * @param {number} [rotation] - the rotation of the page. + */ + fixAndSetPosition(rotation?: number | undefined): void; + /** + * Convert a screen translation into a page one. + * @param {number} x + * @param {number} y + */ + screenToPageTranslation(x: number, y: number): any[]; + /** + * Convert a page translation into a screen one. + * @param {number} x + * @param {number} y + */ + pageTranslationToScreen(x: number, y: number): any[]; + get parentScale(): any; + get parentRotation(): number; + get parentDimensions(): number[]; + /** + * Set the dimensions of this editor. + * @param {number} width + * @param {number} height + */ + setDims(width: number, height: number): void; + fixDims(): void; + /** + * Get the translation used to position this editor when it's created. + * @returns {Array<number>} + */ + getInitialTranslation(): Array<number>; + /** + * Called when the alt text dialog is closed. + */ + altTextFinish(): void; + /** + * Add a toolbar for this editor. + * @returns {Promise<EditorToolbar|null>} + */ + addEditToolbar(): Promise<EditorToolbar | null>; + removeEditToolbar(): void; + addContainer(container: any): void; + getClientDimensions(): DOMRect; + addAltTextButton(): Promise<void>; + /** + * Set the alt text data. + */ + set altTextData(data: any); + get altTextData(): any; + get guessedAltText(): any; + setGuessedAltText(text: any): Promise<void>; + serializeAltText(isForCopying: any): any; + hasAltText(): boolean; + hasAltTextData(): any; + /** + * Render this editor in a div. + * @returns {HTMLDivElement | null} + */ + render(): HTMLDivElement | null; + /** + * Onpointerdown callback. + * @param {PointerEvent} event + */ + pointerdown(event: PointerEvent): void; + moveInDOM(): void; + _setParentAndPosition(parent: any, x: any, y: any): void; + /** + * Convert the current rect into a page one. + * @param {number} tx - x-translation in screen coordinates. + * @param {number} ty - y-translation in screen coordinates. + * @param {number} [rotation] - the rotation of the page. + */ + getRect(tx: number, ty: number, rotation?: number | undefined): any[]; + getRectInCurrentCoords(rect: any, pageHeight: any): any[]; + /** + * Executed once this editor has been rendered. + */ + onceAdded(): void; + /** + * Check if the editor contains something. + * @returns {boolean} + */ + isEmpty(): boolean; + /** + * Enable edit mode. + */ + enableEditMode(): void; + /** + * Disable edit mode. + */ + disableEditMode(): void; + /** + * Check if the editor is edited. + * @returns {boolean} + */ + isInEditMode(): boolean; + /** + * If it returns true, then this editor handles the keyboard + * events itself. + * @returns {boolean} + */ + shouldGetKeyboardEvents(): boolean; + /** + * Check if this editor needs to be rebuilt or not. + * @returns {boolean} + */ + needsToBeRebuilt(): boolean; + /** + * Rebuild the editor in case it has been removed on undo. + * + * To implement in subclasses. + */ + rebuild(): void; + /** + * Rotate the editor. + * @param {number} angle + */ + rotate(_angle: any): void; + /** + * Serialize the editor when it has been deleted. + * @returns {Object} + */ + serializeDeleted(): Object; + /** + * Serialize the editor. + * The result of the serialization will be used to construct a + * new annotation to add to the pdf document. + * + * To implement in subclasses. + * @param {boolean} [isForCopying] + * @param {Object | null} [context] + * @returns {Object | null} + */ + serialize(isForCopying?: boolean | undefined, context?: Object | null | undefined): Object | null; + /** + * Check if an existing annotation associated with this editor has been + * modified. + * @returns {boolean} + */ + get hasBeenModified(): boolean; + /** + * Remove this editor. + * It's used on ctrl+backspace action. + */ + remove(): void; + /** + * @returns {boolean} true if this editor can be resized. + */ + get isResizable(): boolean; + /** + * Add the resizers to this editor. + */ + makeResizable(): void; + get toolbarPosition(): null; + /** + * onkeydown callback. + * @param {KeyboardEvent} event + */ + keydown(event: KeyboardEvent): void; + _resizeWithKeyboard(x: any, y: any): void; + _stopResizingWithKeyboard(): void; + /** + * Select this editor. + */ + select(): void; + /** + * Unselect this editor. + */ + unselect(): void; + /** + * Update some parameters which have been changed through the UI. + * @param {number} type + * @param {*} value + */ + updateParams(type: number, value: any): void; + /** + * When the user disables the editing mode some editors can change some of + * their properties. + */ + disableEditing(): void; + /** + * When the user enables the editing mode some editors can change some of + * their properties. + */ + enableEditing(): void; + /** + * The editor is about to be edited. + */ + enterInEditMode(): void; + /** + * @returns {HTMLElement | null} the element requiring an alt text. + */ + getImageForAltText(): HTMLElement | null; + /** + * Get the div which really contains the displayed content. + * @returns {HTMLDivElement | undefined} + */ + get contentDiv(): HTMLDivElement | undefined; + /** + * When set to true, it means that this editor is currently edited. + * @param {boolean} value + */ + set isEditing(value: boolean); + /** + * If true then the editor is currently edited. + * @type {boolean} + */ + get isEditing(): boolean; + /** + * Set the aspect ratio to use when resizing. + * @param {number} width + * @param {number} height + */ + setAspectRatio(width: number, height: number): void; + /** + * Get the data to report to the telemetry when the editor is added. + * @returns {Object} + */ + get telemetryInitialData(): Object; + /** + * The telemetry data to use when saving/printing. + * @returns {Object|null} + */ + get telemetryFinalData(): Object | null; + _reportTelemetry(data: any, mustWait?: boolean): void; + /** + * Show or hide this editor. + * @param {boolean|undefined} visible + */ + show(visible?: boolean | undefined): void; + enable(): void; + disable(): void; + /** + * Render an annotation in the annotation layer. + * @param {Object} annotation + * @returns {HTMLElement|null} + */ + renderAnnotationElement(annotation: Object): HTMLElement | null; + resetAnnotationElement(annotation: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/freetext.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/freetext.d.ts new file mode 100644 index 0000000000..cd7cd66ef6 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/freetext.d.ts @@ -0,0 +1,54 @@ +/** + * Basic text editor in order to create a FreeTex annotation. + */ +declare class FreeTextEditor extends AnnotationEditor { + static _freeTextDefaultContent: string; + static _internalPadding: number; + static _defaultColor: null; + static _defaultFontSize: number; + static get _keyboardManager(): any; + static _type: string; + static _editorType: number; + /** @inheritdoc */ + static initialize(l10n: any, uiManager: any): void; + /** @inheritdoc */ + static updateDefaultParams(type: any, value: any): void; + /** @inheritdoc */ + static get defaultPropertiesToUpdate(): any[][]; + static "__#19@#getNodeContent"(node: any): any; + static "__#19@#deserializeContent"(content: any): any; + /** @inheritdoc */ + static deserialize(data: any, parent: any, uiManager: any): Promise<AnnotationEditor | null>; + constructor(params: any); + /** @inheritdoc */ + updateParams(type: any, value: any): void; + /** @inheritdoc */ + get propertiesToUpdate(): any[][]; + /** + * Helper to translate the editor with the keyboard when it's empty. + * @param {number} x in page units. + * @param {number} y in page units. + */ + _translateEmpty(x: number, y: number): void; + /** + * Commit the content we have in this editor. + * @returns {undefined} + */ + commit(): undefined; + /** + * ondblclick callback. + * @param {MouseEvent} event + */ + dblclick(event: MouseEvent): void; + editorDivKeydown(event: any): void; + editorDivFocus(event: any): void; + editorDivBlur(event: any): void; + editorDivInput(event: any): void; + editorDiv: HTMLDivElement | undefined; + overlayDiv: HTMLDivElement | undefined; + editorDivPaste(event: any): void; + /** @inheritdoc */ + serialize(isForCopying?: boolean): Object | null; + /** @inheritdoc */ + renderAnnotationElement(annotation: any): HTMLElement | null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/highlight.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/highlight.d.ts new file mode 100644 index 0000000000..aa9c035a90 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/highlight.d.ts @@ -0,0 +1,76 @@ +/** + * Basic draw editor in order to generate an Highlight annotation. + */ +declare class HighlightEditor extends AnnotationEditor { + static _defaultColor: null; + static _defaultOpacity: number; + static _defaultThickness: number; + static _type: string; + static _editorType: number; + static _freeHighlightId: number; + static _freeHighlight: null; + static _freeHighlightClipId: string; + static get _keyboardManager(): any; + static computeTelemetryFinalData(data: any): { + numberOfColors: any; + }; + /** @inheritdoc */ + static initialize(l10n: any, uiManager: any): void; + /** @inheritdoc */ + static updateDefaultParams(type: any, value: any): void; + static get defaultPropertiesToUpdate(): (number | null)[][]; + static "__#25@#rotateBbox"({ x, y, width, height }: { + x: any; + y: any; + width: any; + height: any; + }, angle: any): { + x: any; + y: any; + width: any; + height: any; + }; + static startHighlighting(parent: any, isLTR: any, { target: textLayer, x, y }: { + target: any; + x: any; + y: any; + }): void; + static "__#25@#highlightMove"(parent: any, event: any): void; + static "__#25@#endHighlight"(parent: any, event: any): void; + /** @inheritdoc */ + static deserialize(data: any, parent: any, uiManager: any): Promise<AnnotationEditor | null>; + constructor(params: any); + color: any; + /** @inheritdoc */ + get telemetryInitialData(): { + action: string; + type: string; + color: any; + thickness: any; + methodOfCreation: string; + }; + /** @inheritdoc */ + get telemetryFinalData(): { + type: string; + color: any; + }; + /** @inheritdoc */ + translateInPage(x: any, y: any): void; + /** @inheritdoc */ + updateParams(type: any, value: any): void; + /** @inheritdoc */ + get propertiesToUpdate(): any[][]; + /** @inheritdoc */ + fixAndSetPosition(): void; + /** @inheritdoc */ + getRect(tx: any, ty: any): any[]; + /** @inheritdoc */ + rotate(angle: any): void; + pointerover(): void; + pointerleave(): void; + _moveCaret(direction: any): void; + /** @inheritdoc */ + serialize(isForCopying?: boolean): Object | null; + /** @inheritdoc */ + renderAnnotationElement(annotation: any): null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/ink.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/ink.d.ts new file mode 100644 index 0000000000..a00b23264b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/ink.d.ts @@ -0,0 +1,88 @@ +/** + * Basic draw editor in order to generate an Ink annotation. + */ +declare class InkEditor extends AnnotationEditor { + static _defaultColor: null; + static _defaultOpacity: number; + static _defaultThickness: number; + static _type: string; + static _editorType: number; + /** @inheritdoc */ + static initialize(l10n: any, uiManager: any): void; + /** @inheritdoc */ + static updateDefaultParams(type: any, value: any): void; + /** @inheritdoc */ + static get defaultPropertiesToUpdate(): any[][]; + /** + * Convert into a Path2D. + * @param {Array<Array<number>>} bezier + * @returns {Path2D} + */ + static "__#26@#buildPath2D"(bezier: Array<Array<number>>): Path2D; + static "__#26@#toPDFCoordinates"(points: any, rect: any, rotation: any): any; + static "__#26@#fromPDFCoordinates"(points: any, rect: any, rotation: any): any; + /** @inheritdoc */ + static deserialize(data: any, parent: any, uiManager: any): Promise<AnnotationEditor | null>; + constructor(params: any); + color: any; + thickness: any; + opacity: any; + paths: any[]; + bezierPath2D: any[]; + allRawPaths: any[]; + currentPath: any[]; + scaleFactor: number; + translationX: number; + translationY: number; + /** @inheritdoc */ + updateParams(type: any, value: any): void; + /** @inheritdoc */ + get propertiesToUpdate(): any[][]; + canvas: HTMLCanvasElement | null | undefined; + onScaleChanging(): void; + pointerdownAC: any; + /** + * onpointerdown callback for the canvas we're drawing on. + * @param {PointerEvent} event + */ + canvasPointerdown(event: PointerEvent): void; + /** + * onpointermove callback for the canvas we're drawing on. + * @param {PointerEvent} event + */ + canvasPointermove(event: PointerEvent): void; + /** + * onpointerup callback for the canvas we're drawing on. + * @param {PointerEvent} event + */ + canvasPointerup(event: PointerEvent): void; + /** + * onpointerleave callback for the canvas we're drawing on. + * @param {PointerEvent} event + */ + canvasPointerleave(event: PointerEvent): void; + ctx: CanvasRenderingContext2D | null | undefined; + /** + * When the dimensions of the div change the inner canvas must + * renew its dimensions, hence it must redraw its own contents. + * @param {number} width - the new width of the div + * @param {number} height - the new height of the div + * @returns + */ + setDimensions(width: number, height: number): void; + /** @inheritdoc */ + serialize(): { + annotationType: number; + color: number[]; + thickness: any; + opacity: any; + paths: { + bezier: any; + points: any; + }[]; + pageIndex: number; + rect: any[]; + rotation: number; + structTreeParentId: any; + } | null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/outliner.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/outliner.d.ts new file mode 100644 index 0000000000..3119f20f96 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/outliner.d.ts @@ -0,0 +1,65 @@ +declare class FreeOutliner { + static "__#22@#MIN_DIST": number; + static "__#22@#MIN_DIFF": number; + static "__#22@#MIN": number; + constructor({ x, y }: { + x: any; + y: any; + }, box: any, scaleFactor: any, thickness: any, isLTR: any, innerMargin?: number); + get free(): boolean; + isEmpty(): boolean; + add({ x, y }: { + x: any; + y: any; + }): boolean; + toSVGPath(): string; + getOutlines(): FreeHighlightOutline; +} +declare class Outliner { + /** + * Construct an outliner. + * @param {Array<Object>} boxes - An array of axis-aligned rectangles. + * @param {number} borderWidth - The width of the border of the boxes, it + * allows to make the boxes bigger (or smaller). + * @param {number} innerMargin - The margin between the boxes and the + * outlines. It's important to not have a null innerMargin when we want to + * draw the outline else the stroked outline could be clipped because of its + * width. + * @param {boolean} isLTR - true if we're in LTR mode. It's used to determine + * the last point of the boxes. + */ + constructor(boxes: Array<Object>, borderWidth?: number, innerMargin?: number, isLTR?: boolean); + getOutlines(): HighlightOutline; +} +declare class FreeHighlightOutline extends Outline { + constructor(outline: any, points: any, box: any, scaleFactor: any, innerMargin: any, isLTR: any); + serialize([blX, blY, trX, trY]: [any, any, any, any], rotation: any): { + outline: number[]; + points: number[][]; + }; + get box(): null; + getNewOutline(thickness: any, innerMargin: any): FreeHighlightOutline; +} +declare class HighlightOutline extends Outline { + constructor(outlines: any, box: any); + /** + * Serialize the outlines into the PDF page coordinate system. + * @param {Array<number>} _bbox - the bounding box of the annotation. + * @param {number} _rotation - the rotation of the annotation. + * @returns {Array<Array<number>>} + */ + serialize([blX, blY, trX, trY]: Array<number>, _rotation: number): Array<Array<number>>; + get box(): any; +} +declare class Outline { + /** + * @returns {string} The SVG path of the outline. + */ + toSVGPath(): string; + /** + * @type {Object|null} The bounding box of the outline. + */ + get box(): Object | null; + serialize(_bbox: any, _rotation: any): void; + get free(): any; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/stamp.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/stamp.d.ts new file mode 100644 index 0000000000..5bb4b7af78 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/stamp.d.ts @@ -0,0 +1,44 @@ +/** + * Basic text editor in order to create a FreeTex annotation. + */ +declare class StampEditor extends AnnotationEditor { + static _type: string; + static _editorType: number; + /** @inheritdoc */ + static initialize(l10n: any, uiManager: any): void; + static get supportedTypes(): any; + static get supportedTypesStr(): any; + /** @inheritdoc */ + static isHandlingMimeForPasting(mime: any): any; + /** @inheritdoc */ + static paste(item: any, parent: any): void; + static computeTelemetryFinalData(data: any): { + hasAltText: any; + hasNoAltText: any; + }; + /** @inheritdoc */ + static deserialize(data: any, parent: any, uiManager: any): Promise<AnnotationEditor | null>; + constructor(params: any); + /** @inheritdoc */ + get telemetryFinalData(): { + type: string; + hasAltText: boolean; + }; + mlGuessAltText(imageData?: null, updateAltTextData?: boolean): Promise<any>; + copyCanvas(maxDataDimension: any, maxPreviewDimension: any, createImageData?: boolean): { + canvas: HTMLCanvasElement | null; + width: any; + height: any; + imageData: { + width: any; + height: any; + data: Uint8ClampedArray; + } | null; + }; + /** @inheritdoc */ + getImageForAltText(): null; + /** @inheritdoc */ + serialize(isForCopying?: boolean, context?: null): Object | null; + /** @inheritdoc */ + renderAnnotationElement(annotation: any): null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/toolbar.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/toolbar.d.ts new file mode 100644 index 0000000000..9d05b93e73 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/toolbar.d.ts @@ -0,0 +1,17 @@ +declare class EditorToolbar { + static "__#4@#l10nRemove": null; + static "__#4@#pointerDown"(e: any): void; + constructor(editor: any); + render(): HTMLDivElement; + get div(): null; + hide(): void; + show(): void; + addAltText(altText: any): Promise<void>; + addColorPicker(colorPicker: any): void; + remove(): void; +} +declare class HighlightToolbar { + constructor(uiManager: any); + show(parent: any, boxes: any, isLTR: any): void; + hide(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/tools.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/tools.d.ts new file mode 100644 index 0000000000..87cfde6547 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/editor/tools.d.ts @@ -0,0 +1,405 @@ +/** + * A pdf has several pages and each of them when it will rendered + * will have an AnnotationEditorLayer which will contain the some + * new Annotations associated to an editor in order to modify them. + * + * This class is used to manage all the different layers, editors and + * some action like copy/paste, undo/redo, ... + */ +declare class AnnotationEditorUIManager { + static TRANSLATE_SMALL: number; + static TRANSLATE_BIG: number; + static get _keyboardManager(): any; + constructor(container: any, viewer: any, altTextManager: any, eventBus: any, pdfDocument: any, pageColors: any, highlightColors: any, enableHighlightFloatingButton: any, enableUpdatedAddImage: any, enableNewAltTextWhenAddingImage: any, mlManager: any); + _signal: AbortSignal; + _eventBus: any; + viewParameters: { + realScale: number; + rotation: number; + }; + isShiftKeyDown: boolean; + destroy(): void; + combinedSignal(ac: any): AbortSignal; + get mlManager(): null; + get useNewAltTextFlow(): boolean; + get useNewAltTextWhenAddingImage(): boolean; + get hcmFilter(): any; + get direction(): any; + get highlightColors(): any; + get highlightColorNames(): any; + setMainHighlightColorPicker(colorPicker: any): void; + editAltText(editor: any, firstTime?: boolean): void; + switchToMode(mode: any, callback: any): void; + setPreference(name: any, value: any): void; + onSetPreference({ name, value }: { + name: any; + value: any; + }): void; + onPageChanging({ pageNumber }: { + pageNumber: any; + }): void; + focusMainContainer(): void; + findParent(x: any, y: any): any; + disableUserSelect(value?: boolean): void; + addShouldRescale(editor: any): void; + removeShouldRescale(editor: any): void; + onScaleChanging({ scale }: { + scale: any; + }): void; + onRotationChanging({ pagesRotation }: { + pagesRotation: any; + }): void; + highlightSelection(methodOfCreation?: string): void; + /** + * Add an editor in the annotation storage. + * @param {AnnotationEditor} editor + */ + addToAnnotationStorage(editor: AnnotationEditor): void; + blur(): void; + focus(): void; + addEditListeners(): void; + removeEditListeners(): void; + dragOver(event: any): void; + /** + * Drop callback. + * @param {DragEvent} event + */ + drop(event: DragEvent): void; + /** + * Copy callback. + * @param {ClipboardEvent} event + */ + copy(event: ClipboardEvent): void; + /** + * Cut callback. + * @param {ClipboardEvent} event + */ + cut(event: ClipboardEvent): void; + /** + * Paste callback. + * @param {ClipboardEvent} event + */ + paste(event: ClipboardEvent): Promise<void>; + /** + * Keydown callback. + * @param {KeyboardEvent} event + */ + keydown(event: KeyboardEvent): void; + /** + * Keyup callback. + * @param {KeyboardEvent} event + */ + keyup(event: KeyboardEvent): void; + /** + * Execute an action for a given name. + * For example, the user can click on the "Undo" entry in the context menu + * and it'll trigger the undo action. + */ + onEditingAction({ name }: { + name: any; + }): void; + /** + * Set the editing state. + * It can be useful to temporarily disable it when the user is editing a + * FreeText annotation. + * @param {boolean} isEditing + */ + setEditingState(isEditing: boolean): void; + registerEditorTypes(types: any): void; + /** + * Get an id. + * @returns {string} + */ + getId(): string; + get currentLayer(): any; + getLayer(pageIndex: any): any; + get currentPageIndex(): number; + /** + * Add a new layer for a page which will contains the editors. + * @param {AnnotationEditorLayer} layer + */ + addLayer(layer: AnnotationEditorLayer): void; + /** + * Remove a layer. + * @param {AnnotationEditorLayer} layer + */ + removeLayer(layer: AnnotationEditorLayer): void; + /** + * Change the editor mode (None, FreeText, Ink, ...) + * @param {number} mode + * @param {string|null} editId + * @param {boolean} [isFromKeyboard] - true if the mode change is due to a + * keyboard action. + */ + updateMode(mode: number, editId?: string | null, isFromKeyboard?: boolean | undefined): Promise<void>; + addNewEditorFromKeyboard(): void; + /** + * Update the toolbar if it's required to reflect the tool currently used. + * @param {number} mode + * @returns {undefined} + */ + updateToolbar(mode: number): undefined; + /** + * Update a parameter in the current editor or globally. + * @param {number} type + * @param {*} value + */ + updateParams(type: number, value: any): void; + showAllEditors(type: any, visible: any, updateButton?: boolean): void; + enableWaiting(mustWait?: boolean): void; + /** + * Get all the editors belonging to a given page. + * @param {number} pageIndex + * @returns {Array<AnnotationEditor>} + */ + getEditors(pageIndex: number): Array<AnnotationEditor>; + /** + * Get an editor with the given id. + * @param {string} id + * @returns {AnnotationEditor} + */ + getEditor(id: string): AnnotationEditor; + /** + * Add a new editor. + * @param {AnnotationEditor} editor + */ + addEditor(editor: AnnotationEditor): void; + /** + * Remove an editor. + * @param {AnnotationEditor} editor + */ + removeEditor(editor: AnnotationEditor): void; + /** + * The annotation element with the given id has been deleted. + * @param {AnnotationEditor} editor + */ + addDeletedAnnotationElement(editor: AnnotationEditor): void; + /** + * Check if the annotation element with the given id has been deleted. + * @param {string} annotationElementId + * @returns {boolean} + */ + isDeletedAnnotationElement(annotationElementId: string): boolean; + /** + * The annotation element with the given id have been restored. + * @param {AnnotationEditor} editor + */ + removeDeletedAnnotationElement(editor: AnnotationEditor): void; + /** + * Set the given editor as the active one. + * @param {AnnotationEditor} editor + */ + setActiveEditor(editor: AnnotationEditor): void; + /** + * Update the UI of the active editor. + * @param {AnnotationEditor} editor + */ + updateUI(editor: AnnotationEditor): void; + /** + * Add or remove an editor the current selection. + * @param {AnnotationEditor} editor + */ + toggleSelected(editor: AnnotationEditor): void; + /** + * Set the last selected editor. + * @param {AnnotationEditor} editor + */ + setSelected(editor: AnnotationEditor): void; + /** + * Check if the editor is selected. + * @param {AnnotationEditor} editor + */ + isSelected(editor: AnnotationEditor): boolean; + get firstSelectedEditor(): any; + /** + * Unselect an editor. + * @param {AnnotationEditor} editor + */ + unselect(editor: AnnotationEditor): void; + get hasSelection(): boolean; + get isEnterHandled(): any; + /** + * Undo the last command. + */ + undo(): void; + /** + * Redo the last undoed command. + */ + redo(): void; + /** + * Add a command to execute (cmd) and another one to undo it. + * @param {Object} params + */ + addCommands(params: Object): void; + /** + * Delete the current editor or all. + */ + delete(): void; + commitOrRemove(): void; + hasSomethingToControl(): boolean; + /** + * Select all the editors. + */ + selectAll(): void; + /** + * Unselect all the selected editors. + */ + unselectAll(): void; + translateSelectedEditors(x: any, y: any, noCommit?: boolean): void; + /** + * Set up the drag session for moving the selected editors. + */ + setUpDragSession(): void; + /** + * Ends the drag session. + * @returns {boolean} true if at least one editor has been moved. + */ + endDragSession(): boolean; + /** + * Drag the set of selected editors. + * @param {number} tx + * @param {number} ty + */ + dragSelectedEditors(tx: number, ty: number): void; + /** + * Rebuild the editor (usually on undo/redo actions) on a potentially + * non-rendered page. + * @param {AnnotationEditor} editor + */ + rebuild(editor: AnnotationEditor): void; + get isEditorHandlingKeyboard(): any; + /** + * Is the current editor the one passed as argument? + * @param {AnnotationEditor} editor + * @returns + */ + isActive(editor: AnnotationEditor): editor is never; + /** + * Get the current active editor. + * @returns {AnnotationEditor|null} + */ + getActive(): AnnotationEditor | null; + /** + * Get the current editor mode. + * @returns {number} + */ + getMode(): number; + get imageManager(): any; + getSelectionBoxes(textLayer: any): { + x: number; + y: number; + width: number; + height: number; + }[] | null; + addChangedExistingAnnotation({ annotationElementId, id }: { + annotationElementId: any; + id: any; + }): void; + removeChangedExistingAnnotation({ annotationElementId }: { + annotationElementId: any; + }): void; + renderAnnotationElement(annotation: any): void; +} +declare function bindEvents(obj: any, element: any, names: any): void; +declare class ColorManager { + static _colorsMapping: Map<string, number[]>; + get _colors(): any; + /** + * In High Contrast Mode, the color on the screen is not always the + * real color used in the pdf. + * For example in some cases white can appear to be black but when saving + * we want to have white. + * @param {string} color + * @returns {Array<number>} + */ + convert(color: string): Array<number>; + /** + * An input element must have its color value as a hex string + * and not as color name. + * So this function converts a name into an hex string. + * @param {string} name + * @returns {string} + */ + getHexCode(name: string): string; +} +/** + * Class to handle undo/redo. + * Commands are just saved in a buffer. + * If we hit some memory issues we could likely use a circular buffer. + * It has to be used as a singleton. + */ +declare class CommandManager { + constructor(maxSize?: number); + /** + * @typedef {Object} addOptions + * @property {function} cmd + * @property {function} undo + * @property {function} [post] + * @property {boolean} mustExec + * @property {number} type + * @property {boolean} overwriteIfSameType + * @property {boolean} keepUndo + */ + /** + * Add a new couple of commands to be used in case of redo/undo. + * @param {addOptions} options + */ + add({ cmd, undo, post, mustExec, type, overwriteIfSameType, keepUndo, }: { + cmd: Function; + undo: Function; + post?: Function | undefined; + mustExec: boolean; + type: number; + overwriteIfSameType: boolean; + keepUndo: boolean; + }): void; + /** + * Undo the last command. + */ + undo(): void; + /** + * Redo the last command. + */ + redo(): void; + /** + * Check if there is something to undo. + * @returns {boolean} + */ + hasSomethingToUndo(): boolean; + /** + * Check if there is something to redo. + * @returns {boolean} + */ + hasSomethingToRedo(): boolean; + destroy(): void; +} +/** + * Class to handle the different keyboards shortcuts we can have on mac or + * non-mac OSes. + */ +declare class KeyboardManager { + /** + * Create a new keyboard manager class. + * @param {Array<Array>} callbacks - an array containing an array of shortcuts + * and a callback to call. + * A shortcut is a string like `ctrl+c` or `mac+ctrl+c` for mac OS. + */ + constructor(callbacks: Array<any[]>); + buffer: any[]; + callbacks: Map<any, any>; + allKeys: Set<any>; + /** + * Execute a callback, if any, for a given keyboard event. + * The self is used as `this` in the callback. + * @param {Object} self + * @param {KeyboardEvent} event + * @returns + */ + exec(self: Object, event: KeyboardEvent): void; +} +/** + * Convert a number between 0 and 100 into an hex number between 0 and 255. + * @param {number} opacity + * @return {string} + */ +declare function opacityToHex(opacity: number): string; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/fetch_stream.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/fetch_stream.d.ts new file mode 100644 index 0000000000..301642001c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/fetch_stream.d.ts @@ -0,0 +1,64 @@ +/** @implements {IPDFStream} */ +declare class PDFFetchStream implements IPDFStream { + constructor(source: any); + source: any; + isHttp: boolean; + headers: Headers; + _fullRequestReader: PDFFetchStreamReader | null; + _rangeRequestReaders: any[]; + get _progressiveDataLength(): number; + getFullReader(): PDFFetchStreamReader; + getRangeReader(begin: any, end: any): PDFFetchStreamRangeReader | null; + cancelAllRequests(reason: any): void; +} +/** @implements {IPDFStreamReader} */ +declare class PDFFetchStreamReader implements IPDFStreamReader { + constructor(stream: any); + _stream: any; + _reader: ReadableStreamDefaultReader<Uint8Array> | null; + _loaded: number; + _filename: string | null; + _withCredentials: any; + _contentLength: any; + _headersCapability: any; + _disableRange: any; + _rangeChunkSize: any; + _abortController: AbortController; + _isStreamingSupported: boolean; + _isRangeSupported: boolean; + onProgress: any; + get headersReady(): any; + get filename(): string | null; + get contentLength(): any; + get isRangeSupported(): boolean; + get isStreamingSupported(): boolean; + read(): Promise<{ + value: Uint8Array | undefined; + done: true; + } | { + value: ArrayBufferLike; + done: boolean; + }>; + cancel(reason: any): void; +} +/** @implements {IPDFStreamRangeReader} */ +declare class PDFFetchStreamRangeReader implements IPDFStreamRangeReader { + constructor(stream: any, begin: any, end: any); + _stream: any; + _reader: ReadableStreamDefaultReader<Uint8Array> | null; + _loaded: number; + _withCredentials: any; + _readCapability: any; + _isStreamingSupported: boolean; + _abortController: AbortController; + onProgress: any; + get isStreamingSupported(): boolean; + read(): Promise<{ + value: Uint8Array | undefined; + done: true; + } | { + value: ArrayBufferLike; + done: boolean; + }>; + cancel(reason: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/font_loader.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/font_loader.d.ts new file mode 100644 index 0000000000..796cddb946 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/font_loader.d.ts @@ -0,0 +1,41 @@ +declare class FontFaceObject { + constructor(translatedData: any, { disableFontFace, inspectFont }: { + disableFontFace?: boolean | undefined; + inspectFont?: null | undefined; + }); + compiledGlyphs: any; + disableFontFace: boolean; + _inspectFont: any; + createNativeFontFace(): FontFace | null; + createFontFaceRule(): string | null; + getPathGenerator(objs: any, character: any): any; +} +declare class FontLoader { + constructor({ ownerDocument, styleElement, }: { + ownerDocument?: Document | undefined; + styleElement?: null | undefined; + }); + _document: Document; + nativeFontFaces: Set<any>; + styleElement: HTMLStyleElement | null; + loadingRequests: any[] | undefined; + loadTestFontId: number | undefined; + addNativeFontFace(nativeFontFace: any): void; + removeNativeFontFace(nativeFontFace: any): void; + insertRule(rule: any): void; + clear(): void; + loadSystemFont({ systemFontInfo: info, _inspectFont }: { + systemFontInfo: any; + _inspectFont: any; + }): Promise<void>; + bind(font: any): Promise<void>; + get isFontLoadingAPISupported(): any; + get isSyncFontLoadingSupported(): any; + _queueLoadingCallback(callback: any): { + done: boolean; + complete: () => void; + callback: any; + }; + get _loadTestFont(): any; + _prepareFontLoadEvent(font: any, request: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/metadata.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/metadata.d.ts new file mode 100644 index 0000000000..b7a8e4b699 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/metadata.d.ts @@ -0,0 +1,10 @@ +declare class Metadata { + constructor({ parsedData, rawData }: { + parsedData: any; + rawData: any; + }); + getRaw(): any; + get(name: any): any; + getAll(): any; + has(name: any): any; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network.d.ts new file mode 100644 index 0000000000..6766771875 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network.d.ts @@ -0,0 +1,85 @@ +/** @implements {IPDFStream} */ +declare class PDFNetworkStream implements IPDFStream { + constructor(source: any); + _source: any; + _manager: NetworkManager; + _rangeChunkSize: any; + _fullRequestReader: PDFNetworkStreamFullRequestReader | null; + _rangeRequestReaders: any[]; + _onRangeRequestReaderClosed(reader: any): void; + getFullReader(): PDFNetworkStreamFullRequestReader; + getRangeReader(begin: any, end: any): PDFNetworkStreamRangeRequestReader; + cancelAllRequests(reason: any): void; +} +declare class NetworkManager { + constructor({ url, httpHeaders, withCredentials }: { + url: any; + httpHeaders: any; + withCredentials: any; + }); + url: any; + isHttp: boolean; + headers: Headers; + withCredentials: any; + currXhrId: number; + pendingRequests: any; + requestRange(begin: any, end: any, listeners: any): number; + requestFull(listeners: any): number; + request(args: any): number; + onProgress(xhrId: any, evt: any): void; + onStateChange(xhrId: any, evt: any): void; + getRequestXhr(xhrId: any): any; + isPendingRequest(xhrId: any): boolean; + abortRequest(xhrId: any): void; +} +/** @implements {IPDFStreamReader} */ +declare class PDFNetworkStreamFullRequestReader implements IPDFStreamReader { + constructor(manager: any, source: any); + _manager: any; + _url: any; + _fullRequestId: any; + _headersCapability: any; + _disableRange: any; + _contentLength: any; + _rangeChunkSize: any; + _isStreamingSupported: boolean; + _isRangeSupported: boolean; + _cachedChunks: any[]; + _requests: any[]; + _done: boolean; + _storedError: MissingPDFException | UnexpectedResponseException | undefined; + _filename: string | null; + onProgress: any; + _onHeadersReceived(): void; + _onDone(data: any): void; + _onError(status: any): void; + _onProgress(evt: any): void; + get filename(): string | null; + get isRangeSupported(): boolean; + get isStreamingSupported(): boolean; + get contentLength(): any; + get headersReady(): any; + read(): Promise<any>; + cancel(reason: any): void; + _fullRequestReader: any; +} +/** @implements {IPDFStreamRangeReader} */ +declare class PDFNetworkStreamRangeRequestReader implements IPDFStreamRangeReader { + constructor(manager: any, begin: any, end: any); + _manager: any; + _url: any; + _requestId: any; + _requests: any[]; + _queuedChunk: any; + _done: boolean; + _storedError: MissingPDFException | UnexpectedResponseException | undefined; + onProgress: any; + onClosed: any; + _close(): void; + _onDone(data: any): void; + _onError(status: any): void; + _onProgress(evt: any): void; + get isStreamingSupported(): boolean; + read(): Promise<any>; + cancel(reason: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network_utils.d.ts new file mode 100644 index 0000000000..1e11d865f0 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/network_utils.d.ts @@ -0,0 +1,13 @@ +declare function createHeaders(isHttp: any, httpHeaders: any): Headers; +declare function createResponseStatusError(status: any, url: any): MissingPDFException | UnexpectedResponseException; +declare function extractFilenameFromHeader(responseHeaders: any): string | null; +declare function validateRangeRequestCapabilities({ responseHeaders, isHttp, rangeChunkSize, disableRange, }: { + responseHeaders: any; + isHttp: any; + rangeChunkSize: any; + disableRange: any; +}): { + allowRangeRequests: boolean; + suggestedLength: undefined; +}; +declare function validateResponseStatus(status: any): boolean; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_stream.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_stream.d.ts new file mode 100644 index 0000000000..faf02f9379 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_stream.d.ts @@ -0,0 +1,68 @@ +declare class PDFNodeStream { + constructor(source: any); + source: any; + url: URL; + isHttp: boolean; + isFsUrl: boolean; + headers: Headers; + _fullRequestReader: PDFNodeStreamFsFullReader | PDFNodeStreamFullReader | null; + _rangeRequestReaders: any[]; + get _progressiveDataLength(): number; + getFullReader(): PDFNodeStreamFsFullReader | PDFNodeStreamFullReader; + getRangeReader(start: any, end: any): PDFNodeStreamFsRangeReader | PDFNodeStreamRangeReader | null; + cancelAllRequests(reason: any): void; +} +declare class PDFNodeStreamFsFullReader extends BaseFullReader { +} +declare class PDFNodeStreamFullReader extends BaseFullReader { + _request: any; +} +declare class PDFNodeStreamFsRangeReader extends BaseRangeReader { + constructor(stream: any, start: any, end: any); +} +declare class PDFNodeStreamRangeReader extends BaseRangeReader { + constructor(stream: any, start: any, end: any); + _request: any; +} +declare class BaseFullReader { + constructor(stream: any); + _url: any; + _done: boolean; + _storedError: any; + onProgress: any; + _contentLength: any; + _loaded: number; + _filename: any; + _disableRange: any; + _rangeChunkSize: any; + _isStreamingSupported: boolean; + _isRangeSupported: boolean; + _readableStream: any; + _readCapability: any; + _headersCapability: any; + get headersReady(): any; + get filename(): any; + get contentLength(): any; + get isRangeSupported(): boolean; + get isStreamingSupported(): boolean; + read(): any; + cancel(reason: any): void; + _error(reason: any): void; + _setReadableStream(readableStream: any): void; +} +declare class BaseRangeReader { + constructor(stream: any); + _url: any; + _done: boolean; + _storedError: any; + onProgress: any; + _loaded: number; + _readableStream: any; + _readCapability: any; + _isStreamingSupported: boolean; + get isStreamingSupported(): boolean; + read(): any; + cancel(reason: any): void; + _error(reason: any): void; + _setReadableStream(readableStream: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_utils.d.ts new file mode 100644 index 0000000000..9000122b5b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/node_utils.d.ts @@ -0,0 +1,24 @@ +declare class NodeCanvasFactory extends BaseCanvasFactory { + /** + * @ignore + */ + _createCanvas(width: any, height: any): any; +} +declare class NodeCMapReaderFactory extends BaseCMapReaderFactory { + /** + * @ignore + */ + _fetchData(url: any, compressionType: any): any; +} +declare class NodeFilterFactory extends BaseFilterFactory { +} +declare class NodePackages { + static get promise(): any; + static get(name: any): any; +} +declare class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { + /** + * @ignore + */ + _fetchData(url: any): any; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/optional_content_config.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/optional_content_config.d.ts new file mode 100644 index 0000000000..b38eca5dee --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/optional_content_config.d.ts @@ -0,0 +1,17 @@ +declare class OptionalContentConfig { + constructor(data: any, renderingIntent?: number); + renderingIntent: number; + name: any; + creator: any; + isVisible(group: any): any; + setVisibility(id: any, visible?: boolean): void; + setOCGState({ state, preserveRB }: { + state: any; + preserveRB: any; + }): void; + get hasInitialVisibility(): boolean; + getOrder(): any; + getGroups(): any; + getGroup(id: any): any; + getHash(): string; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/pattern_helper.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/pattern_helper.d.ts new file mode 100644 index 0000000000..d04166e4a9 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/pattern_helper.d.ts @@ -0,0 +1,72 @@ +declare function getShadingPattern(IR: any): RadialAxialShadingPattern | MeshShadingPattern | DummyShadingPattern; +declare namespace PathType { + let FILL: string; + let STROKE: string; + let SHADING: string; +} +declare class TilingPattern { + static MAX_PATTERN_SIZE: number; + constructor(IR: any, color: any, ctx: any, canvasGraphicsFactory: any, baseTransform: any); + operatorList: any; + matrix: any; + bbox: any; + xstep: any; + ystep: any; + paintType: any; + tilingType: any; + color: any; + ctx: any; + canvasGraphicsFactory: any; + baseTransform: any; + createPatternCanvas(owner: any): { + canvas: any; + scaleX: any; + scaleY: any; + offsetX: any; + offsetY: any; + }; + getSizeAndScale(step: any, realOutputSize: any, scale: any): { + scale: any; + size: number; + }; + clipBbox(graphics: any, x0: any, y0: any, x1: any, y1: any): void; + setFillAndStrokeStyleToContext(graphics: any, paintType: any, color: any): void; + getPattern(ctx: any, owner: any, inverse: any, pathType: any): any; +} +declare class RadialAxialShadingPattern extends BaseShadingPattern { + constructor(IR: any); + _type: any; + _bbox: any; + _colorStops: any; + _p0: any; + _p1: any; + _r0: any; + _r1: any; + matrix: any; + _createGradient(ctx: any): any; + getPattern(ctx: any, owner: any, inverse: any, pathType: any): any; +} +declare class MeshShadingPattern extends BaseShadingPattern { + constructor(IR: any); + _coords: any; + _colors: any; + _figures: any; + _bounds: any; + _bbox: any; + _background: any; + matrix: any; + _createMeshCanvas(combinedScale: any, backgroundColor: any, cachedCanvases: any): { + canvas: any; + offsetX: number; + offsetY: number; + scaleX: number; + scaleY: number; + }; + getPattern(ctx: any, owner: any, inverse: any, pathType: any): any; +} +declare class DummyShadingPattern extends BaseShadingPattern { + getPattern(): string; +} +declare class BaseShadingPattern { + getPattern(ctx: any, owner: any, inverse: any, pathType: any): any; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/text_layer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/text_layer.d.ts new file mode 100644 index 0000000000..a4cbd44ce6 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/text_layer.d.ts @@ -0,0 +1,82 @@ +declare type TextLayerParameters = { + /** + * - Text content to + * render, i.e. the value returned by the page's `streamTextContent` or + * `getTextContent` method. + */ + textContentSource: ReadableStream | TextContent; + /** + * - The DOM node that will contain the text + * runs. + */ + container: HTMLElement; + /** + * - The target viewport to properly layout + * the text runs. + */ + viewport: PageViewport; +}; +declare type TextLayerUpdateParameters = { + /** + * - The target viewport to properly layout + * the text runs. + */ + viewport: PageViewport; + /** + * - Callback invoked before the textLayer is + * updated in the DOM. + */ + onBefore?: Function | undefined; +}; +declare class TextLayer { + static "__#46@#ascentCache": Map<any, any>; + static "__#46@#canvasContexts": Map<any, any>; + static "__#46@#canvasCtxFonts": WeakMap<object, any>; + static "__#46@#minFontSize": null; + static "__#46@#pendingTextLayers": Set<any>; + static get fontFamilyMap(): any; + /** + * Clean-up global textLayer data. + * @returns {undefined} + */ + static cleanup(): undefined; + static "__#46@#getCtx"(lang?: null): any; + static "__#46@#ensureCtxFont"(ctx: any, size: any, family: any): void; + /** + * Compute the minimum font size enforced by the browser. + */ + static "__#46@#ensureMinFontSizeComputed"(): void; + static "__#46@#getAscent"(fontFamily: any, lang: any): any; + /** + * @param {TextLayerParameters} options + */ + constructor({ textContentSource, container, viewport }: TextLayerParameters); + /** + * Render the textLayer. + * @returns {Promise} + */ + render(): Promise<any>; + /** + * Update a previously rendered textLayer, if necessary. + * @param {TextLayerUpdateParameters} options + * @returns {undefined} + */ + update({ viewport, onBefore }: TextLayerUpdateParameters): undefined; + /** + * Cancel rendering of the textLayer. + * @returns {undefined} + */ + cancel(): undefined; + /** + * @type {Array<HTMLElement>} HTML elements that correspond to the text items + * of the textContent input. + * This is output and will initially be set to an empty array. + */ + get textDivs(): HTMLElement[]; + /** + * @type {Array<string>} Strings that correspond to the `str` property of + * the text items of the textContent input. + * This is output and will initially be set to an empty array + */ + get textContentItemsStr(): string[]; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/transport_stream.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/transport_stream.d.ts new file mode 100644 index 0000000000..fd7706d864 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/transport_stream.d.ts @@ -0,0 +1,63 @@ +/** @implements {IPDFStream} */ +declare class PDFDataTransportStream implements IPDFStream { + constructor(pdfDataRangeTransport: any, { disableRange, disableStream }: { + disableRange?: boolean | undefined; + disableStream?: boolean | undefined; + }); + _queuedChunks: ArrayBuffer[]; + _progressiveDone: any; + _contentDispositionFilename: any; + _pdfDataRangeTransport: any; + _isStreamingSupported: boolean; + _isRangeSupported: boolean; + _contentLength: any; + _fullRequestReader: any; + _rangeReaders: any[]; + _onReceiveData({ begin, chunk }: { + begin: any; + chunk: any; + }): void; + get _progressiveDataLength(): any; + _onProgress(evt: any): void; + _onProgressiveDone(): void; + _removeRangeReader(reader: any): void; + getFullReader(): PDFDataTransportStreamReader; + getRangeReader(begin: any, end: any): PDFDataTransportStreamRangeReader | null; + cancelAllRequests(reason: any): void; +} +/** @implements {IPDFStreamReader} */ +declare class PDFDataTransportStreamReader implements IPDFStreamReader { + constructor(stream: any, queuedChunks: any, progressiveDone?: boolean, contentDispositionFilename?: null); + _stream: any; + _done: boolean; + _filename: any; + _queuedChunks: any; + _loaded: number; + _requests: any[]; + _headersReady: Promise<void>; + onProgress: any; + _enqueue(chunk: any): void; + get headersReady(): Promise<void>; + get filename(): any; + get isRangeSupported(): any; + get isStreamingSupported(): any; + get contentLength(): any; + read(): Promise<any>; + cancel(reason: any): void; + progressiveDone(): void; +} +/** @implements {IPDFStreamRangeReader} */ +declare class PDFDataTransportStreamRangeReader implements IPDFStreamRangeReader { + constructor(stream: any, begin: any, end: any); + _stream: any; + _begin: any; + _end: any; + _queuedChunk: any; + _requests: any[]; + _done: boolean; + onProgress: any; + _enqueue(chunk: any): void; + get isStreamingSupported(): boolean; + read(): Promise<any>; + cancel(reason: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/worker_options.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/worker_options.d.ts new file mode 100644 index 0000000000..cfacc37383 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/worker_options.d.ts @@ -0,0 +1,25 @@ +declare class GlobalWorkerOptions { + static "__#41@#port": null; + static "__#41@#src": string; + /** + * @param {Worker | null} workerPort - Defines global port for worker process. + * Overrides the `workerSrc` option. + */ + static set workerPort(val: Worker | null); + /** + * @type {Worker | null} + */ + static get workerPort(): Worker | null; + /** + * @param {string} workerSrc - A string containing the path and filename of + * the worker file. + * + * NOTE: The `workerSrc` option should always be set, in order to prevent + * any issues when using the PDF.js library. + */ + static set workerSrc(val: string); + /** + * @type {string} + */ + static get workerSrc(): string; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_layer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_layer.d.ts new file mode 100644 index 0000000000..212d332085 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_layer.d.ts @@ -0,0 +1,44 @@ +declare type XfaLayerParameters = { + viewport: PageViewport; + div: HTMLDivElement; + xfaHtml: Object; + annotationStorage?: AnnotationStorage | undefined; + linkService: IPDFLinkService; + /** + * - (default value is 'display'). + */ + intent?: string | undefined; +}; +/** + * @typedef {Object} XfaLayerParameters + * @property {PageViewport} viewport + * @property {HTMLDivElement} div + * @property {Object} xfaHtml + * @property {AnnotationStorage} [annotationStorage] + * @property {IPDFLinkService} linkService + * @property {string} [intent] - (default value is 'display'). + */ +declare class XfaLayer { + static setupStorage(html: any, id: any, element: any, storage: any, intent: any): void; + static setAttributes({ html, element, storage, intent, linkService }: { + html: any; + element: any; + storage?: null | undefined; + intent: any; + linkService: any; + }): void; + /** + * Render the XFA layer. + * + * @param {XfaLayerParameters} parameters + */ + static render(parameters: XfaLayerParameters): { + textDivs: Text[]; + }; + /** + * Update the XFA layer. + * + * @param {XfaLayerParameters} parameters + */ + static update(parameters: XfaLayerParameters): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_text.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_text.d.ts new file mode 100644 index 0000000000..6a8286e725 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/display/xfa_text.d.ts @@ -0,0 +1,20 @@ +/** @typedef {TextContent} TextContent */ +declare class XfaText { + /** + * Walk an XFA tree and create an array of text nodes that is compatible + * with a regular PDFs TextContent. Currently, only TextItem.str is supported, + * all other fields and styles haven't been implemented. + * + * @param {Object} xfa - An XFA fake DOM object. + * + * @returns {TextContent} + */ + static textContent(xfa: Object): TextContent; + /** + * @param {string} name - DOM node name. (lower case) + * + * @returns {boolean} true if the DOM node should have a corresponding text + * node. + */ + static shouldBuildText(name: string): boolean; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/interfaces.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/interfaces.d.ts new file mode 100644 index 0000000000..c6e8c29935 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/interfaces.d.ts @@ -0,0 +1,118 @@ +/** + * Interface that represents PDF data transport. If possible, it allows + * progressively load entire or fragment of the PDF binary data. + * + * @interface + */ +declare class IPDFStream { + /** + * Gets a reader for the entire PDF data. + * @returns {IPDFStreamReader} + */ + getFullReader(): IPDFStreamReader; + /** + * Gets a reader for the range of the PDF data. + * @param {number} begin - the start offset of the data. + * @param {number} end - the end offset of the data. + * @returns {IPDFStreamRangeReader} + */ + getRangeReader(begin: number, end: number): IPDFStreamRangeReader | null; + /** + * Cancels all opened reader and closes all their opened requests. + * @param {Object} reason - the reason for cancelling + */ + cancelAllRequests(reason: Object): void; +} +/** + * Interface for a PDF binary data fragment reader. + * + * @interface + */ +declare class IPDFStreamRangeReader { + /** + * Sets or gets the progress callback. The callback can be useful when the + * isStreamingSupported property of the object is defined as false. + * The callback is called with one parameter: an object with the loaded + * property. + */ + onProgress: any; + /** + * Gets ability of the stream to progressively load binary data. + * @type {boolean} + */ + get isStreamingSupported(): boolean; + /** + * Requests a chunk of the binary data. The method returns the promise, which + * is resolved into object with properties "value" and "done". If the done + * is set to true, then the stream has reached its end, otherwise the value + * contains binary data. Cancelled requests will be resolved with the done is + * set to true. + * @returns {Promise} + */ + read(): Promise<any>; + /** + * Cancels all pending read requests and closes the stream. + * @param {Object} reason + */ + cancel(reason: Object): void; +} +/** + * Interface for a PDF binary data reader. + * + * @interface + */ +declare class IPDFStreamReader { + /** + * Sets or gets the progress callback. The callback can be useful when the + * isStreamingSupported property of the object is defined as false. + * The callback is called with one parameter: an object with the loaded and + * total properties. + */ + onProgress: any; + /** + * Gets a promise that is resolved when the headers and other metadata of + * the PDF data stream are available. + * @type {Promise} + */ + get headersReady(): Promise<any>; + /** + * Gets the Content-Disposition filename. It is defined after the headersReady + * promise is resolved. + * @type {string|null} The filename, or `null` if the Content-Disposition + * header is missing/invalid. + */ + get filename(): string | null; + /** + * Gets PDF binary data length. It is defined after the headersReady promise + * is resolved. + * @type {number} The data length (or 0 if unknown). + */ + get contentLength(): number; + /** + * Gets ability of the stream to handle range requests. It is defined after + * the headersReady promise is resolved. Rejected when the reader is cancelled + * or an error occurs. + * @type {boolean} + */ + get isRangeSupported(): boolean; + /** + * Gets ability of the stream to progressively load binary data. It is defined + * after the headersReady promise is resolved. + * @type {boolean} + */ + get isStreamingSupported(): boolean; + /** + * Requests a chunk of the binary data. The method returns the promise, which + * is resolved into object with properties "value" and "done". If the done + * is set to true, then the stream has reached its end, otherwise the value + * contains binary data. Cancelled requests will be resolved with the done is + * set to true. + * @returns {Promise} + */ + read(): Promise<any>; + /** + * Cancels all pending read requests and closes the stream. + * @param {Object} reason + */ + cancel(reason: Object): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/image_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/image_utils.d.ts new file mode 100644 index 0000000000..1cadfb8a16 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/image_utils.d.ts @@ -0,0 +1,17 @@ +declare function convertBlackAndWhiteToRGBA({ src, srcPos, dest, width, height, nonBlackColor, inverseDecode, }: { + src: any; + srcPos?: number | undefined; + dest: any; + width: any; + height: any; + nonBlackColor?: number | undefined; + inverseDecode?: boolean | undefined; +}): { + srcPos: number; + destPos: number; +}; +declare function convertToRGBA(params: any): { + srcPos: number; + destPos: number; +} | null; +declare function grayToRGBA(src: any, dest: any): void; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/message_handler.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/message_handler.d.ts new file mode 100644 index 0000000000..ecfd072274 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/message_handler.d.ts @@ -0,0 +1,42 @@ +declare class MessageHandler { + constructor(sourceName: any, targetName: any, comObj: any); + sourceName: any; + targetName: any; + comObj: any; + callbackId: number; + streamId: number; + streamSinks: any; + streamControllers: any; + callbackCapabilities: any; + actionHandler: any; + _onComObjOnMessage: (event: any) => void; + on(actionName: any, handler: any): void; + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * @param {string} actionName - Action to call. + * @param {JSON} data - JSON data to send. + * @param {Array} [transfers] - List of transfers/ArrayBuffers. + */ + send(actionName: string, data: JSON, transfers?: any[] | undefined): void; + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * Expects that the other side will callback with the response. + * @param {string} actionName - Action to call. + * @param {JSON} data - JSON data to send. + * @param {Array} [transfers] - List of transfers/ArrayBuffers. + * @returns {Promise} Promise to be resolved with response data. + */ + sendWithPromise(actionName: string, data: JSON, transfers?: any[] | undefined): Promise<any>; + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * Expect that the other side will callback to signal 'start_complete'. + * @param {string} actionName - Action to call. + * @param {JSON} data - JSON data to send. + * @param {Object} queueingStrategy - Strategy to signal backpressure based on + * internal queue. + * @param {Array} [transfers] - List of transfers/ArrayBuffers. + * @returns {ReadableStream} ReadableStream to read data in chunks. + */ + sendWithStream(actionName: string, data: JSON, queueingStrategy: Object, transfers?: any[] | undefined): ReadableStream; + destroy(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/murmurhash3.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/murmurhash3.d.ts new file mode 100644 index 0000000000..9634e60e42 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/murmurhash3.d.ts @@ -0,0 +1,7 @@ +declare class MurmurHash3_64 { + constructor(seed: any); + h1: number; + h2: number; + update(input: any): void; + hexdigest(): string; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/scripting_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/scripting_utils.d.ts new file mode 100644 index 0000000000..b0dc299af7 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/scripting_utils.d.ts @@ -0,0 +1,16 @@ +declare class ColorConverters { + static CMYK_G([c, y, m, k]: [any, any, any, any]): (string | number)[]; + static G_CMYK([g]: [any]): (string | number)[]; + static G_RGB([g]: [any]): any[]; + static G_rgb([g]: [any]): any[]; + static G_HTML([g]: [any]): string; + static RGB_G([r, g, b]: [any, any, any]): (string | number)[]; + static RGB_rgb(color: any): any; + static RGB_HTML(color: any): string; + static T_HTML(): string; + static T_rgb(): null[]; + static CMYK_RGB([c, y, m, k]: [any, any, any, any]): (string | number)[]; + static CMYK_rgb([c, y, m, k]: [any, any, any, any]): number[]; + static CMYK_HTML(components: any): string; + static RGB_CMYK([r, g, b]: [any, any, any]): (string | number)[]; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/util.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/util.d.ts new file mode 100644 index 0000000000..41611e666e --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/shared/util.d.ts @@ -0,0 +1,403 @@ +declare const AbortException_base: any; +/** + * Error used to indicate task cancellation. + */ +declare class AbortException extends AbortException_base { + [x: string]: any; + constructor(msg: any); +} +declare namespace AnnotationActionEventType { + let E: string; + let X: string; + let D: string; + let U: string; + let Fo: string; + let Bl: string; + let PO: string; + let PC: string; + let PV: string; + let PI: string; + let K: string; + let F: string; + let V: string; + let C: string; +} +declare namespace AnnotationBorderStyleType { + let SOLID: number; + let DASHED: number; + let BEVELED: number; + let INSET: number; + let UNDERLINE: number; +} +declare namespace AnnotationEditorParamsType { + let RESIZE: number; + let CREATE: number; + let FREETEXT_SIZE: number; + let FREETEXT_COLOR: number; + let FREETEXT_OPACITY: number; + let INK_COLOR: number; + let INK_THICKNESS: number; + let INK_OPACITY: number; + let HIGHLIGHT_COLOR: number; + let HIGHLIGHT_DEFAULT_COLOR: number; + let HIGHLIGHT_THICKNESS: number; + let HIGHLIGHT_FREE: number; + let HIGHLIGHT_SHOW_ALL: number; +} +declare const AnnotationEditorPrefix: "pdfjs_internal_editor_"; +declare namespace AnnotationEditorType { + let DISABLE: number; + let NONE: number; + let FREETEXT: number; + let HIGHLIGHT: number; + let STAMP: number; + let INK: number; +} +declare namespace AnnotationFieldFlag { + let READONLY: number; + let REQUIRED: number; + let NOEXPORT: number; + let MULTILINE: number; + let PASSWORD: number; + let NOTOGGLETOOFF: number; + let RADIO: number; + let PUSHBUTTON: number; + let COMBO: number; + let EDIT: number; + let SORT: number; + let FILESELECT: number; + let MULTISELECT: number; + let DONOTSPELLCHECK: number; + let DONOTSCROLL: number; + let COMB: number; + let RICHTEXT: number; + let RADIOSINUNISON: number; + let COMMITONSELCHANGE: number; +} +declare namespace AnnotationFlag { + let INVISIBLE: number; + let HIDDEN: number; + let PRINT: number; + let NOZOOM: number; + let NOROTATE: number; + let NOVIEW: number; + let READONLY_1: number; + //export { READONLY_1 as READONLY }; + let LOCKED: number; + let TOGGLENOVIEW: number; + let LOCKEDCONTENTS: number; +} +declare namespace AnnotationMode { + let DISABLE_1: number; + //export { DISABLE_1 as DISABLE }; + let ENABLE: number; + let ENABLE_FORMS: number; + let ENABLE_STORAGE: number; +} +declare const AnnotationPrefix: "pdfjs_internal_id_"; +declare namespace AnnotationReplyType { + let GROUP: string; + let REPLY: string; +} +declare namespace AnnotationType { + let TEXT: number; + let LINK: number; + let FREETEXT_1: number; + //export { FREETEXT_1 as FREETEXT }; + let LINE: number; + let SQUARE: number; + let CIRCLE: number; + let POLYGON: number; + let POLYLINE: number; + let HIGHLIGHT_1: number; + //export { HIGHLIGHT_1 as HIGHLIGHT }; + let UNDERLINE_1: number; + //export { UNDERLINE_1 as UNDERLINE }; + let SQUIGGLY: number; + let STRIKEOUT: number; + let STAMP_1: number; + //export { STAMP_1 as STAMP }; + let CARET: number; + let INK_1: number; + //export { INK_1 as INK }; + let POPUP: number; + let FILEATTACHMENT: number; + let SOUND: number; + let MOVIE: number; + let WIDGET: number; + let SCREEN: number; + let PRINTERMARK: number; + let TRAPNET: number; + let WATERMARK: number; + let THREED: number; + let REDACT: number; +} +declare function assert(cond: any, msg: any): void; +/** + * @type {any} + */ +declare const BaseException: any; +declare const BASELINE_FACTOR: number; +declare function bytesToString(bytes: any): string; +declare namespace CMapCompressionType { + let NONE_1: number; + //export { NONE_1 as NONE }; + let BINARY: number; +} +/** + * Attempts to create a valid absolute URL. + * + * @param {URL|string} url - An absolute, or relative, URL. + * @param {URL|string} [baseUrl] - An absolute URL. + * @param {Object} [options] + * @returns Either a valid {URL}, or `null` otherwise. + */ +declare function createValidAbsoluteUrl(url: URL | string, baseUrl?: string | URL | undefined, options?: Object | undefined): URL | null; +declare namespace DocumentActionEventType { + let WC: string; + let WS: string; + let DS: string; + let WP: string; + let DP: string; +} +declare class FeatureTest { + static get isLittleEndian(): any; + static get isEvalSupported(): any; + static get isOffscreenCanvasSupported(): any; + static get platform(): any; + static get isCSSRoundSupported(): any; +} +declare const FONT_IDENTITY_MATRIX: number[]; +declare namespace FontRenderOps { + let BEZIER_CURVE_TO: number; + let MOVE_TO: number; + let LINE_TO: number; + let QUADRATIC_CURVE_TO: number; + let RESTORE: number; + let SAVE: number; + let SCALE: number; + let TRANSFORM: number; + let TRANSLATE: number; +} +declare const FormatError_base: any; +/** + * Error caused during parsing PDF data. + */ +declare class FormatError extends FormatError_base { + [x: string]: any; + constructor(msg: any); +} +declare function getModificationDate(date?: Date): string; +declare function getUuid(): string; +declare function getVerbosityLevel(): number; +declare const IDENTITY_MATRIX: number[]; +declare namespace ImageKind { + let GRAYSCALE_1BPP: number; + let RGB_24BPP: number; + let RGBA_32BPP: number; +} +declare function info(msg: any): void; +declare const InvalidPDFException_base: any; +declare class InvalidPDFException extends InvalidPDFException_base { + [x: string]: any; + constructor(msg: any); +} +declare function isArrayEqual(arr1: any, arr2: any): boolean; +declare const isNodeJS: any; +declare const LINE_DESCENT_FACTOR: 0.35; +declare const LINE_FACTOR: 1.35; +declare const MAX_IMAGE_SIZE_TO_CACHE: 10000000; +declare const MissingPDFException_base: any; +declare class MissingPDFException extends MissingPDFException_base { + [x: string]: any; + constructor(msg: any); +} +declare function normalizeUnicode(str: any): any; +declare function objectFromMap(map: any): any; +declare function objectSize(obj: any): number; +declare namespace OPS { + let dependency: number; + let setLineWidth: number; + let setLineCap: number; + let setLineJoin: number; + let setMiterLimit: number; + let setDash: number; + let setRenderingIntent: number; + let setFlatness: number; + let setGState: number; + let save: number; + let restore: number; + let transform: number; + let moveTo: number; + let lineTo: number; + let curveTo: number; + let curveTo2: number; + let curveTo3: number; + let closePath: number; + let rectangle: number; + let stroke: number; + let closeStroke: number; + let fill: number; + let eoFill: number; + let fillStroke: number; + let eoFillStroke: number; + let closeFillStroke: number; + let closeEOFillStroke: number; + let endPath: number; + let clip: number; + let eoClip: number; + let beginText: number; + let endText: number; + let setCharSpacing: number; + let setWordSpacing: number; + let setHScale: number; + let setLeading: number; + let setFont: number; + let setTextRenderingMode: number; + let setTextRise: number; + let moveText: number; + let setLeadingMoveText: number; + let setTextMatrix: number; + let nextLine: number; + let showText: number; + let showSpacedText: number; + let nextLineShowText: number; + let nextLineSetSpacingShowText: number; + let setCharWidth: number; + let setCharWidthAndBounds: number; + let setStrokeColorSpace: number; + let setFillColorSpace: number; + let setStrokeColor: number; + let setStrokeColorN: number; + let setFillColor: number; + let setFillColorN: number; + let setStrokeGray: number; + let setFillGray: number; + let setStrokeRGBColor: number; + let setFillRGBColor: number; + let setStrokeCMYKColor: number; + let setFillCMYKColor: number; + let shadingFill: number; + let beginInlineImage: number; + let beginImageData: number; + let endInlineImage: number; + let paintXObject: number; + let markPoint: number; + let markPointProps: number; + let beginMarkedContent: number; + let beginMarkedContentProps: number; + let endMarkedContent: number; + let beginCompat: number; + let endCompat: number; + let paintFormXObjectBegin: number; + let paintFormXObjectEnd: number; + let beginGroup: number; + let endGroup: number; + let beginAnnotation: number; + let endAnnotation: number; + let paintImageMaskXObject: number; + let paintImageMaskXObjectGroup: number; + let paintImageXObject: number; + let paintInlineImageXObject: number; + let paintInlineImageXObjectGroup: number; + let paintImageXObjectRepeat: number; + let paintImageMaskXObjectRepeat: number; + let paintSolidColorImageMask: number; + let constructPath: number; + let setStrokeTransparent: number; + let setFillTransparent: number; +} +declare namespace PageActionEventType { + let O: string; + let C_1: string; + //export { C_1 as C }; +} +declare const PasswordException_base: any; +declare class PasswordException extends PasswordException_base { + [x: string]: any; + constructor(msg: any, code: any); + code: any; +} +declare namespace PasswordResponses { + let NEED_PASSWORD: number; + let INCORRECT_PASSWORD: number; +} +declare namespace PermissionFlag { + let PRINT_1: number; + //export { PRINT_1 as PRINT }; + let MODIFY_CONTENTS: number; + let COPY: number; + let MODIFY_ANNOTATIONS: number; + let FILL_INTERACTIVE_FORMS: number; + let COPY_FOR_ACCESSIBILITY: number; + let ASSEMBLE: number; + let PRINT_HIGH_QUALITY: number; +} +declare namespace RenderingIntentFlag { + let ANY: number; + let DISPLAY: number; + let PRINT_2: number; + //export { PRINT_2 as PRINT }; + let SAVE_1: number; + //export { SAVE_1 as SAVE }; + let ANNOTATIONS_FORMS: number; + let ANNOTATIONS_STORAGE: number; + let ANNOTATIONS_DISABLE: number; + let IS_EDITING: number; + let OPLIST: number; +} +declare function setVerbosityLevel(level: any): void; +declare function shadow(obj: any, prop: any, value: any, nonSerializable?: boolean): any; +declare function string32(value: any): string; +declare function stringToBytes(str: any): Uint8Array; +declare function stringToPDFString(str: any): string; +declare function stringToUTF8String(str: any): string; +declare namespace TextRenderingMode { + let FILL: number; + let STROKE: number; + let FILL_STROKE: number; + let INVISIBLE_1: number; + //export { INVISIBLE_1 as INVISIBLE }; + let FILL_ADD_TO_PATH: number; + let STROKE_ADD_TO_PATH: number; + let FILL_STROKE_ADD_TO_PATH: number; + let ADD_TO_PATH: number; + let FILL_STROKE_MASK: number; + let ADD_TO_PATH_FLAG: number; +} +declare const UnexpectedResponseException_base: any; +declare class UnexpectedResponseException extends UnexpectedResponseException_base { + [x: string]: any; + constructor(msg: any, status: any); + status: any; +} +declare const UnknownErrorException_base: any; +declare class UnknownErrorException extends UnknownErrorException_base { + [x: string]: any; + constructor(msg: any, details: any); + details: any; +} +declare function unreachable(msg: any): void; +declare function utf8StringToString(str: any): string; +declare class Util { + static makeHexColor(r: any, g: any, b: any): string; + static scaleMinMax(transform: any, minMax: any): void; + static transform(m1: any, m2: any): any[]; + static applyTransform(p: any, m: any): any[]; + static applyInverseTransform(p: any, m: any): number[]; + static getAxialAlignedBoundingBox(r: any, m: any): number[]; + static inverseTransform(m: any): number[]; + static singularValueDecompose2dScale(m: any): number[]; + static normalizeRect(rect: any): any; + static intersect(rect1: any, rect2: any): number[] | null; + static "__#1@#getExtremumOnCurve"(x0: any, x1: any, x2: any, x3: any, y0: any, y1: any, y2: any, y3: any, t: any, minMax: any): void; + static "__#1@#getExtremum"(x0: any, x1: any, x2: any, x3: any, y0: any, y1: any, y2: any, y3: any, a: any, b: any, c: any, minMax: any): void; + static bezierBoundingBox(x0: any, y0: any, x1: any, y1: any, x2: any, y2: any, x3: any, y3: any, minMax: any): any; +} +declare namespace VerbosityLevel { + let ERRORS: number; + let WARNINGS: number; + let INFOS: number; +} +declare function warn(msg: any): void; +//export {}; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_editor_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_editor_layer_builder.d.ts new file mode 100644 index 0000000000..07cbfee2f8 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_editor_layer_builder.d.ts @@ -0,0 +1,43 @@ +declare type AnnotationEditorLayerBuilderOptions = { + uiManager?: AnnotationEditorUIManager | undefined; + pdfPage: PDFPageProxy; + l10n?: IL10n | undefined; + structTreeLayer?: StructTreeLayerBuilder; + accessibilityManager?: TextAccessibilityManager | undefined; + annotationLayer?: AnnotationLayer | undefined; + textLayer?: any; + drawLayer?: any; + onAppend?: Function | undefined; +}; +/** + * @typedef {Object} AnnotationEditorLayerBuilderOptions + * @property {AnnotationEditorUIManager} [uiManager] + * @property {PDFPageProxy} pdfPage + * @property {IL10n} [l10n] + * @property {StructTreeLayerBuilder} [structTreeLayer] + * @property {TextAccessibilityManager} [accessibilityManager] + * @property {AnnotationLayer} [annotationLayer] + * @property {TextLayer} [textLayer] + * @property {DrawLayer} [drawLayer] + * @property {function} [onAppend] + */ +declare class AnnotationEditorLayerBuilder { + /** + * @param {AnnotationEditorLayerBuilderOptions} options + */ + constructor(options: AnnotationEditorLayerBuilderOptions); + pdfPage: PDFPageProxy; + accessibilityManager: TextAccessibilityManager | undefined; + l10n: IL10n | GenericL10n | undefined; + annotationEditorLayer: AnnotationEditorLayer | null; + div: HTMLDivElement | null; + _cancelled: boolean; + /** + * @param {PageViewport} viewport + * @param {string} intent (default value is 'display') + */ + render(viewport: PageViewport, intent?: string): Promise<void>; + cancel(): void; + hide(): void; + show(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_layer_builder.d.ts new file mode 100644 index 0000000000..b738b45f41 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/annotation_layer_builder.d.ts @@ -0,0 +1,74 @@ +declare type AnnotationLayerBuilderOptions = { + pdfPage: PDFPageProxy; + annotationStorage?: AnnotationStorage | undefined; + /** + * - Path for image resources, mainly + * for annotation icons. Include trailing slash. + */ + imageResourcesPath?: string | undefined; + renderForms: boolean; + linkService: IPDFLinkService; + downloadManager?: IDownloadManager | undefined; + enableScripting?: boolean | undefined; + hasJSActionsPromise?: Promise<boolean> | undefined; + fieldObjectsPromise?: Promise<{ + [x: string]: Object[]; + } | null> | undefined; + annotationCanvasMap?: Map<string, HTMLCanvasElement> | undefined; + accessibilityManager?: TextAccessibilityManager | undefined; + annotationEditorUIManager?: AnnotationEditorUIManager | undefined; + onAppend?: Function | undefined; +}; +/** + * @typedef {Object} AnnotationLayerBuilderOptions + * @property {PDFPageProxy} pdfPage + * @property {AnnotationStorage} [annotationStorage] + * @property {string} [imageResourcesPath] - Path for image resources, mainly + * for annotation icons. Include trailing slash. + * @property {boolean} renderForms + * @property {IPDFLinkService} linkService + * @property {IDownloadManager} [downloadManager] + * @property {boolean} [enableScripting] + * @property {Promise<boolean>} [hasJSActionsPromise] + * @property {Promise<Object<string, Array<Object>> | null>} + * [fieldObjectsPromise] + * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] + * @property {TextAccessibilityManager} [accessibilityManager] + * @property {AnnotationEditorUIManager} [annotationEditorUIManager] + * @property {function} [onAppend] + */ +declare class AnnotationLayerBuilder { + /** + * @param {AnnotationLayerBuilderOptions} options + */ + constructor({ pdfPage, linkService, downloadManager, annotationStorage, imageResourcesPath, renderForms, enableScripting, hasJSActionsPromise, fieldObjectsPromise, annotationCanvasMap, accessibilityManager, annotationEditorUIManager, onAppend, }: AnnotationLayerBuilderOptions); + pdfPage: PDFPageProxy; + linkService: IPDFLinkService; + downloadManager: IDownloadManager | undefined; + imageResourcesPath: string; + renderForms: boolean; + annotationStorage: AnnotationStorage; + enableScripting: boolean; + _hasJSActionsPromise: Promise<boolean>; + _fieldObjectsPromise: Promise<{ + [x: string]: Object[]; + } | null>; + _annotationCanvasMap: Map<string, HTMLCanvasElement>; + _accessibilityManager: TextAccessibilityManager; + _annotationEditorUIManager: AnnotationEditorUIManager; + annotationLayer: AnnotationLayer | null; + div: HTMLDivElement | null; + _cancelled: boolean; + _eventBus: any; + /** + * @param {PageViewport} viewport + * @param {Object} options + * @param {string} intent (default value is 'display') + * @returns {Promise<void>} A promise that is resolved when rendering of the + * annotations is complete. + */ + render(viewport: PageViewport, options: Object, intent?: string): Promise<void>; + cancel(): void; + hide(): void; + hasEditableAnnotations(): boolean; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/app_options.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/app_options.d.ts new file mode 100644 index 0000000000..efaff7d2a8 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/app_options.d.ts @@ -0,0 +1,16 @@ +declare class AppOptions { + static eventBus: any; + static "__#65@#opts": Map<any, any>; + static get(name: any): any; + static getAll(kind?: null, defaultOnly?: boolean): any; + static set(name: any, value: any): void; + static setAll(options: any, prefs?: boolean): void; +} +declare namespace OptionKind { + let BROWSER: number; + let VIEWER: number; + let API: number; + let WORKER: number; + let EVENT_DISPATCH: number; + let PREFERENCE: number; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/download_manager.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/download_manager.d.ts new file mode 100644 index 0000000000..1a43f0798a --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/download_manager.d.ts @@ -0,0 +1,11 @@ +/** + * @implements {IDownloadManager} + */ +declare class DownloadManager implements IDownloadManager { + downloadData(data: any, filename: any, contentType: any): void; + /** + * @returns {boolean} Indicating if the data was opened. + */ + openOrDownloadData(data: any, filename: any, dest?: null): boolean; + download(data: any, url: any, filename: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/draw_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/draw_layer_builder.d.ts new file mode 100644 index 0000000000..192378e18a --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/draw_layer_builder.d.ts @@ -0,0 +1,22 @@ +declare type DrawLayerBuilderOptions = { + pageIndex: number; +}; +/** + * @typedef {Object} DrawLayerBuilderOptions + * @property {number} pageIndex + */ +declare class DrawLayerBuilder { + /** + * @param {DrawLayerBuilderOptions} options + */ + constructor(options: DrawLayerBuilderOptions); + pageIndex: number; + /** + * @param {string} intent (default value is 'display') + */ + render(intent?: string): Promise<void>; + cancel(): void; + _cancelled: boolean | undefined; + setParent(parent: any): void; + getDrawLayer(): null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/event_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/event_utils.d.ts new file mode 100644 index 0000000000..11369427b9 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/event_utils.d.ts @@ -0,0 +1,75 @@ +declare type WaitOnEventOrTimeoutParameters = { + /** + * - The event target, can for example be: + * `window`, `document`, a DOM element, or an {EventBus} instance. + */ + target: Object; + /** + * - The name of the event. + */ + name: string; + /** + * - The delay, in milliseconds, after which the + * timeout occurs (if the event wasn't already dispatched). + */ + delay: number; +}; +/** + * Simple event bus for an application. Listeners are attached using the `on` + * and `off` methods. To raise an event, the `dispatch` method shall be used. + */ +declare class EventBus { + /** + * @param {string} eventName + * @param {function} listener + * @param {Object} [options] + */ + on(eventName: string, listener: Function, options?: Object | undefined): void; + /** + * @param {string} eventName + * @param {function} listener + * @param {Object} [options] + */ + off(eventName: string, listener: Function, options?: Object | undefined): void; + /** + * @param {string} eventName + * @param {Object} data + */ + dispatch(eventName: string, data: Object): void; + /** + * @ignore + */ + _on(eventName: any, listener: any, options?: null): void; + /** + * @ignore + */ + _off(eventName: any, listener: any, options?: null): void; +} +/** + * NOTE: Only used in the Firefox build-in pdf viewer. + */ +declare class FirefoxEventBus extends EventBus { + constructor(globalEventNames: any, externalServices: any, isInAutomation: any); + dispatch(eventName: any, data: any): void; +} +/** + * @typedef {Object} WaitOnEventOrTimeoutParameters + * @property {Object} target - The event target, can for example be: + * `window`, `document`, a DOM element, or an {EventBus} instance. + * @property {string} name - The name of the event. + * @property {number} delay - The delay, in milliseconds, after which the + * timeout occurs (if the event wasn't already dispatched). + */ +/** + * Allows waiting for an event or a timeout, whichever occurs first. + * Can be used to ensure that an action always occurs, even when an event + * arrives late or not at all. + * + * @param {WaitOnEventOrTimeoutParameters} + * @returns {Promise} A promise that is resolved with a {WaitOnType} value. + */ +declare function waitOnEventOrTimeout({ target, name, delay }: WaitOnEventOrTimeoutParameters): Promise<any>; +declare namespace WaitOnType { + let EVENT: string; + let TIMEOUT: string; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/generic_scripting.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/generic_scripting.d.ts new file mode 100644 index 0000000000..634d33ff55 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/generic_scripting.d.ts @@ -0,0 +1,8 @@ +declare function docProperties(pdfDocument: any): Promise<any>; +declare class GenericScripting { + constructor(sandboxBundleSrc: any); + _ready: Promise<any>; + createSandbox(data: any): Promise<void>; + dispatchEventInSandbox(event: any): Promise<void>; + destroySandbox(): Promise<void>; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/genericl10n.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/genericl10n.d.ts new file mode 100644 index 0000000000..bc439fe86b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/genericl10n.d.ts @@ -0,0 +1,20 @@ +/** + * @implements {IL10n} + */ +declare class GenericL10n extends L10n implements IL10n { + /** + * Generate the bundles for Fluent. + * @param {String} defaultLang - The fallback language to use for + * translations. + * @param {String} baseLang - The base language to use for translations. + */ + static "__#62@#generateBundles"(defaultLang: string, baseLang: string): AsyncGenerator<any, void, unknown>; + static "__#62@#createBundle"(lang: any, baseURL: any, paths: any): Promise<any>; + static "__#62@#getPaths"(): Promise<{ + baseURL: any; + paths: any; + }>; + static "__#62@#generateBundlesFallback"(lang: any): AsyncGenerator<any, void, unknown>; + static "__#62@#createBundleFallback"(lang: any): Promise<any>; + constructor(lang: any); +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/interfaces.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/interfaces.d.ts new file mode 100644 index 0000000000..da030505f5 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/interfaces.d.ts @@ -0,0 +1,165 @@ +declare type RenderingStates = any; +/** + * @interface + */ +declare class IDownloadManager { + /** + * @param {Uint8Array} data + * @param {string} filename + * @param {string} [contentType] + */ + downloadData(data: Uint8Array, filename: string, contentType?: string | undefined): void; + /** + * @param {Uint8Array} data + * @param {string} filename + * @param {string | null} [dest] + * @returns {boolean} Indicating if the data was opened. + */ + openOrDownloadData(data: Uint8Array, filename: string, dest?: string | null | undefined): boolean; + /** + * @param {Uint8Array} data + * @param {string} url + * @param {string} filename + */ + download(data: Uint8Array, url: string, filename: string): void; +} +/** + * @interface + */ +declare class IL10n { + /** + * @returns {string} - The current locale. + */ + getLanguage(): string; + /** + * @returns {string} - 'rtl' or 'ltr'. + */ + getDirection(): string; + /** + * Translates text identified by the key and adds/formats data using the args + * property bag. If the key was not found, translation falls back to the + * fallback text. + * @param {Array | string} ids + * @param {Object | null} [args] + * @param {string} [fallback] + * @returns {Promise<string>} + */ + get(ids: any[] | string, args?: Object | null | undefined, fallback?: string | undefined): Promise<string>; + /** + * Translates HTML element. + * @param {HTMLElement} element + * @returns {Promise<void>} + */ + translate(element: HTMLElement): Promise<void>; + /** + * Pause the localization. + */ + pause(): void; + /** + * Resume the localization. + */ + resume(): void; +} +/** @typedef {PDFPageProxy} PDFPageProxy */ +/** @typedef {PageViewport} PageViewport */ +/** @typedef {RenderingStates} RenderingStates */ +/** + * @interface + */ +declare class IPDFLinkService { + /** + * @type {number} + */ + get pagesCount(): number; + /** + * @param {number} value + */ + set page(value: number); + /** + * @type {number} + */ + get page(): number; + /** + * @param {number} value + */ + set rotation(value: number); + /** + * @type {number} + */ + get rotation(): number; + /** + * @type {boolean} + */ + get isInPresentationMode(): boolean; + /** + * @param {boolean} value + */ + set externalLinkEnabled(value: boolean); + /** + * @type {boolean} + */ + get externalLinkEnabled(): boolean; + /** + * @param {string|Array} dest - The named, or explicit, PDF destination. + */ + goToDestination(dest: string | any[]): Promise<void>; + /** + * @param {number|string} val - The page number, or page label. + */ + goToPage(val: number | string): void; + /** + * @param {HTMLAnchorElement} link + * @param {string} url + * @param {boolean} [newWindow] + */ + addLinkAttributes(link: HTMLAnchorElement, url: string, newWindow?: boolean | undefined): void; + /** + * @param dest - The PDF destination object. + * @returns {string} The hyperlink to the PDF object. + */ + getDestinationHash(dest: any): string; + /** + * @param hash - The PDF parameters/hash. + * @returns {string} The hyperlink to the PDF object. + */ + getAnchorUrl(hash: any): string; + /** + * @param {string} hash + */ + setHash(hash: string): void; + /** + * @param {string} action + */ + executeNamedAction(action: string): void; + /** + * @param {Object} action + */ + executeSetOCGState(action: Object): void; +} +/** + * @interface + */ +declare class IPDFPrintServiceFactory { + static initGlobals(): void; + static get supportsPrinting(): boolean; + static createPrintService(): void; +} +/** + * @interface + */ +declare class IRenderableView { + /** @type {function | null} */ + resume: Function | null; + /** + * @type {string} - Unique ID for rendering queue. + */ + get renderingId(): string; + /** + * @type {RenderingStates} + */ + get renderingState(): any; + /** + * @returns {Promise} Resolved on draw completion. + */ + draw(): Promise<any>; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/l10n.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/l10n.d.ts new file mode 100644 index 0000000000..2859f10410 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/l10n.d.ts @@ -0,0 +1,30 @@ +/** + * NOTE: The L10n-implementations should use lowercase language-codes + * internally. + * @implements {IL10n} + */ +declare class L10n implements IL10n { + static "__#61@#fixupLangCode"(langCode: any): any; + static "__#61@#isRTL"(lang: any): boolean; + constructor({ lang, isRTL }: { + lang: any; + isRTL: any; + }, l10n?: null); + _setL10n(l10n: any): void; + /** @inheritdoc */ + getLanguage(): any; + /** @inheritdoc */ + getDirection(): string; + /** @inheritdoc */ + get(ids: any, args: null | undefined, fallback: any): Promise<any>; + /** @inheritdoc */ + translate(element: any): Promise<void>; + /** @inheritdoc */ + translateOnce(element: any): Promise<void>; + /** @inheritdoc */ + destroy(): Promise<void>; + /** @inheritdoc */ + pause(): void; + /** @inheritdoc */ + resume(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_controller.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_controller.d.ts new file mode 100644 index 0000000000..991ca1483c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_controller.d.ts @@ -0,0 +1,128 @@ +declare type PDFFindControllerOptions = { + /** + * - The navigation/linking service. + */ + linkService: IPDFLinkService; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - True if the matches + * count must be updated on progress or only when the last page is reached. + * The default value is `true`. + */ + updateMatchesCountOnProgress?: boolean | undefined; +}; +declare namespace FindState { + let FOUND: number; + let NOT_FOUND: number; + let WRAPPED: number; + let PENDING: number; +} +/** + * @typedef {Object} PDFFindControllerOptions + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {EventBus} eventBus - The application event bus. + * @property {boolean} [updateMatchesCountOnProgress] - True if the matches + * count must be updated on progress or only when the last page is reached. + * The default value is `true`. + */ +/** + * Provides search functionality to find a given string in a PDF document. + */ +declare class PDFFindController { + /** + * @param {PDFFindControllerOptions} options + */ + constructor({ linkService, eventBus, updateMatchesCountOnProgress }: PDFFindControllerOptions); + _linkService: IPDFLinkService; + _eventBus: EventBus; + /** + * Callback used to check if a `pageNumber` is currently visible. + * @type {function} + */ + onIsPageVisible: Function; + get highlightMatches(): boolean | undefined; + get pageMatches(): any[] | undefined; + get pageMatchesLength(): any[] | undefined; + get selected(): { + pageIdx: number; + matchIdx: number; + } | undefined; + get state(): null; + /** + * Set a reference to the PDF document in order to search it. + * Note that searching is not possible if this method is not called. + * + * @param {PDFDocumentProxy} pdfDocument - The PDF document to search. + */ + setDocument(pdfDocument: PDFDocumentProxy): void; + _pdfDocument: PDFDocumentProxy | null | undefined; + _dirtyMatch: boolean | undefined; + _findTimeout: any; + _highlightMatches: boolean | undefined; + /** + * @typedef {Object} PDFFindControllerScrollMatchIntoViewParams + * @property {HTMLElement} element + * @property {number} selectedLeft + * @property {number} pageIndex + * @property {number} matchIndex + */ + /** + * Scroll the current match into view. + * @param {PDFFindControllerScrollMatchIntoViewParams} + */ + scrollMatchIntoView({ element, selectedLeft, pageIndex, matchIndex, }: { + element: HTMLElement; + selectedLeft: number; + pageIndex: number; + matchIndex: number; + }): void; + _scrollMatches: boolean | undefined; + _pageMatches: any[] | undefined; + _pageMatchesLength: any[] | undefined; + _selected: { + pageIdx: number; + matchIdx: number; + } | undefined; + _offset: { + pageIdx: null; + matchIdx: null; + wrapped: boolean; + } | undefined; + _extractTextPromises: any[] | undefined; + _pageContents: any[] | undefined; + _pageDiffs: any[] | undefined; + _hasDiacritics: any[] | undefined; + _matchesCountTotal: number | undefined; + _pagesToSearch: number | null | undefined; + _pendingFindMatches: Set<any> | undefined; + _resumePageIdx: any; + _firstPageCapability: any; + _rawQuery: any; + /** + * @typedef {Object} FindMatch + * @property {number} index - The start of the matched text in the page's + * string contents. + * @property {number} length - The length of the matched text. + */ + /** + * @param {string | string[]} query - The search query. + * @param {string} pageContent - The text content of the page to search in. + * @param {number} pageIndex - The index of the page that is being processed. + * @returns {FindMatch[] | undefined} An array of matches in the provided + * page. + */ + match(query: string | string[], pageContent: string, pageIndex: number): { + /** + * - The start of the matched text in the page's + * string contents. + */ + index: number; + /** + * - The length of the matched text. + */ + length: number; + }[] | undefined; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_utils.d.ts new file mode 100644 index 0000000000..a71acd5250 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_find_utils.d.ts @@ -0,0 +1,16 @@ +declare namespace CharacterType { + let SPACE: number; + let ALPHA_LETTER: number; + let PUNCT: number; + let HAN_LETTER: number; + let KATAKANA_LETTER: number; + let HIRAGANA_LETTER: number; + let HALFWIDTH_KATAKANA_LETTER: number; + let THAI_LETTER: number; +} +/** + * This function is based on the word-break detection implemented in: + * https://hg.mozilla.org/mozilla-central/file/tip/intl/lwbrk/WordBreaker.cpp + */ +declare function getCharacterType(charCode: any): number; +declare function getNormalizeWithNFKC(): any; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_history.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_history.d.ts new file mode 100644 index 0000000000..deb82c0a8f --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_history.d.ts @@ -0,0 +1,113 @@ +declare type PDFHistoryOptions = { + /** + * - The navigation/linking service. + */ + linkService: IPDFLinkService; + /** + * - The application event bus. + */ + eventBus: EventBus; +}; +declare type InitializeParameters = { + /** + * - The PDF document's unique fingerprint. + */ + fingerprint: string; + /** + * - Reset the browsing history. + */ + resetHistory?: boolean | undefined; + /** + * - Attempt to update the document URL, with + * the current hash, when pushing/replacing browser history entries. + */ + updateUrl?: boolean | undefined; +}; +declare type PushParameters = { + /** + * - The named destination. If absent, a + * stringified version of `explicitDest` is used. + */ + namedDest?: string | undefined; + /** + * - The explicit destination array. + */ + explicitDest: any[]; + /** + * - The page to which the destination points. + */ + pageNumber: number; +}; +declare function isDestArraysEqual(firstDest: any, secondDest: any): boolean; +declare function isDestHashesEqual(destHash: any, pushHash: any): boolean; +declare class PDFHistory { + /** + * @param {PDFHistoryOptions} options + */ + constructor({ linkService, eventBus }: PDFHistoryOptions); + linkService: IPDFLinkService; + eventBus: EventBus; + _initialized: boolean; + _fingerprint: string; + _isPagesLoaded: boolean; + /** + * Initialize the history for the PDF document, using either the current + * browser history entry or the document hash, whichever is present. + * @param {InitializeParameters} params + */ + initialize({ fingerprint, resetHistory, updateUrl }: InitializeParameters): void; + _updateUrl: boolean | undefined; + _popStateInProgress: boolean | undefined; + _blockHashChange: number | undefined; + _currentHash: string | undefined; + _numPositionUpdates: number | undefined; + _uid: any; + _maxUid: any; + _destination: any; + _position: { + hash: any; + page: number; + first: any; + rotation: any; + } | null | undefined; + _initialRotation: any; + _initialBookmark: any; + /** + * Reset the current `PDFHistory` instance, and consequently prevent any + * further updates and/or navigation of the browser history. + */ + reset(): void; + _updateViewareaTimeout: any; + /** + * Push an internal destination to the browser history. + * @param {PushParameters} + */ + push({ namedDest, explicitDest, pageNumber }: PushParameters): void; + /** + * Push a page to the browser history; generally the `push` method should be + * used instead. + * @param {number} pageNumber + */ + pushPage(pageNumber: number): void; + /** + * Push the current position to the browser history. + */ + pushCurrentPosition(): void; + /** + * Go back one step in the browser history. + * NOTE: Avoids navigating away from the document, useful for "named actions". + */ + back(): void; + /** + * Go forward one step in the browser history. + * NOTE: Avoids navigating away from the document, useful for "named actions". + */ + forward(): void; + /** + * @type {boolean} Indicating if the user is currently moving through the + * browser history, useful e.g. for skipping the next 'hashchange' event. + */ + get popStateInProgress(): boolean; + get initialBookmark(): any; + get initialRotation(): any; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_link_service.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_link_service.d.ts new file mode 100644 index 0000000000..bc59c1428c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_link_service.d.ts @@ -0,0 +1,138 @@ +declare type PDFLinkServiceOptions = { + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - Specifies the `target` attribute + * for external links. Must use one of the values from {LinkTarget}. + * Defaults to using no target. + */ + externalLinkTarget?: number | undefined; + /** + * - Specifies the `rel` attribute for + * external links. Defaults to stripping the referrer. + */ + externalLinkRel?: string | undefined; + /** + * - Ignores the zoom argument, + * thus preserving the current zoom level in the viewer, when navigating + * to internal destinations. The default value is `false`. + */ + ignoreDestinationZoom?: boolean | undefined; +}; +declare namespace LinkTarget { + let NONE: number; + let SELF: number; + let BLANK: number; + let PARENT: number; + let TOP: number; +} +/** + * @typedef {Object} PDFLinkServiceOptions + * @property {EventBus} eventBus - The application event bus. + * @property {number} [externalLinkTarget] - Specifies the `target` attribute + * for external links. Must use one of the values from {LinkTarget}. + * Defaults to using no target. + * @property {string} [externalLinkRel] - Specifies the `rel` attribute for + * external links. Defaults to stripping the referrer. + * @property {boolean} [ignoreDestinationZoom] - Ignores the zoom argument, + * thus preserving the current zoom level in the viewer, when navigating + * to internal destinations. The default value is `false`. + */ +/** + * Performs navigation functions inside PDF, such as opening specified page, + * or destination. + * @implements {IPDFLinkService} + */ +declare class PDFLinkService implements IPDFLinkService { + static "__#58@#isValidExplicitDest"(dest: any): boolean; + /** + * @param {PDFLinkServiceOptions} options + */ + constructor({ eventBus, externalLinkTarget, externalLinkRel, ignoreDestinationZoom, }?: PDFLinkServiceOptions); + externalLinkEnabled: boolean; + eventBus: EventBus; + externalLinkTarget: number; + externalLinkRel: string; + _ignoreDestinationZoom: boolean; + baseUrl: any; + pdfDocument: any; + pdfViewer: any; + pdfHistory: any; + setDocument(pdfDocument: any, baseUrl?: null): void; + setViewer(pdfViewer: any): void; + setHistory(pdfHistory: any): void; + /** + * @type {number} + */ + get pagesCount(): number; + /** + * @param {number} value + */ + set page(value: number); + /** + * @type {number} + */ + get page(): number; + /** + * @param {number} value + */ + set rotation(value: number); + /** + * @type {number} + */ + get rotation(): number; + /** + * @type {boolean} + */ + get isInPresentationMode(): boolean; + /** + * This method will, when available, also update the browser history. + * + * @param {string|Array} dest - The named, or explicit, PDF destination. + */ + goToDestination(dest: string | any[]): Promise<void>; + /** + * This method will, when available, also update the browser history. + * + * @param {number|string} val - The page number, or page label. + */ + goToPage(val: number | string): void; + /** + * Adds various attributes (href, title, target, rel) to hyperlinks. + * @param {HTMLAnchorElement} link + * @param {string} url + * @param {boolean} [newWindow] + */ + addLinkAttributes(link: HTMLAnchorElement, url: string, newWindow?: boolean | undefined): void; + /** + * @param {string|Array} dest - The PDF destination object. + * @returns {string} The hyperlink to the PDF object. + */ + getDestinationHash(dest: string | any[]): string; + /** + * Prefix the full url on anchor links to make sure that links are resolved + * relative to the current URL instead of the one defined in <base href>. + * @param {string} anchor - The anchor hash, including the #. + * @returns {string} The hyperlink to the PDF object. + */ + getAnchorUrl(anchor: string): string; + /** + * @param {string} hash + */ + setHash(hash: string): void; + /** + * @param {string} action + */ + executeNamedAction(action: string): void; + /** + * @param {Object} action + */ + executeSetOCGState(action: Object): Promise<void>; +} +/** + * @implements {IPDFLinkService} + */ +declare class SimpleLinkService extends PDFLinkService implements IPDFLinkService { +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_page_view.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_page_view.d.ts new file mode 100644 index 0000000000..67c536f802 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_page_view.d.ts @@ -0,0 +1,194 @@ +declare type PDFPageViewOptions = { + /** + * - The viewer element. + */ + container?: HTMLDivElement | undefined; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The page unique ID (normally its number). + */ + id: number; + /** + * - The page scale display. + */ + scale?: number | undefined; + /** + * - The page viewport. + */ + defaultViewport: PageViewport; + /** + * - + * A promise that is resolved with an {@link OptionalContentConfig} instance. + * The default value is `null`. + */ + optionalContentConfigPromise?: Promise<OptionalContentConfig> | undefined; + /** + * - The rendering queue object. + */ + renderingQueue?: PDFRenderingQueue | undefined; + /** + * - Controls if the text layer used for + * selection and searching is created. The constants from {TextLayerMode} + * should be used. The default value is `TextLayerMode.ENABLE`. + */ + textLayerMode?: number | undefined; + /** + * - Controls if the annotation layer is + * created, and if interactive form elements or `AnnotationStorage`-data are + * being rendered. The constants from {@link AnnotationMode} should be used; + * see also {@link RenderParameters} and {@link GetOperatorListParameters}. + * The default value is `AnnotationMode.ENABLE_FORMS`. + */ + annotationMode?: number | undefined; + /** + * - Path for image resources, mainly + * for annotation icons. Include trailing slash. + */ + imageResourcesPath?: string | undefined; + /** + * - The maximum supported canvas size in + * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for + * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + */ + maxCanvasPixels?: number | undefined; + /** + * - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + */ + pageColors?: Object | undefined; + /** + * - Localization service. + */ + l10n?: IL10n | undefined; + /** + * - The object that is used to lookup + * the necessary layer-properties. + */ + layerProperties?: Object | undefined; + /** + * - Enables hardware acceleration for + * rendering. The default value is `false`. + */ + enableHWA?: boolean | undefined; +}; +/** + * @implements {IRenderableView} + */ +declare class PDFPageView implements IRenderableView { + /** + * @param {PDFPageViewOptions} options + */ + constructor(options: PDFPageViewOptions); + id: number; + renderingId: string; + pdfPage: any; + pageLabel: string | null; + rotation: number; + scale: number; + viewport: PageViewport; + pdfPageRotate: number; + _optionalContentConfigPromise: Promise<OptionalContentConfig> | null; + imageResourcesPath: string; + maxCanvasPixels: any; + pageColors: Object | null; + eventBus: EventBus; + renderingQueue: PDFRenderingQueue | undefined; + l10n: IL10n | GenericL10n | undefined; + renderTask: any; + resume: (() => void) | null; + _isStandalone: boolean | undefined; + _container: HTMLDivElement | undefined; + _annotationCanvasMap: any; + annotationLayer: AnnotationLayerBuilder | null; + annotationEditorLayer: any; + textLayer: TextLayerBuilder | null; + zoomLayer: ParentNode | null; + xfaLayer: XfaLayerBuilder | null; + structTreeLayer: any; + drawLayer: any; + div: HTMLDivElement; + set renderingState(state: number); + get renderingState(): number; + setPdfPage(pdfPage: any): void; + destroy(): void; + hasEditableAnnotations(): boolean; + get _textHighlighter(): any; + /** + * @private + */ + private _resetZoomLayer; + reset({ keepZoomLayer, keepAnnotationLayer, keepAnnotationEditorLayer, keepXfaLayer, keepTextLayer, }?: { + keepZoomLayer?: boolean | undefined; + keepAnnotationLayer?: boolean | undefined; + keepAnnotationEditorLayer?: boolean | undefined; + keepXfaLayer?: boolean | undefined; + keepTextLayer?: boolean | undefined; + }): void; + toggleEditingMode(isEditing: any): void; + /** + * @typedef {Object} PDFPageViewUpdateParameters + * @property {number} [scale] The new scale, if specified. + * @property {number} [rotation] The new rotation, if specified. + * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] + * A promise that is resolved with an {@link OptionalContentConfig} + * instance. The default value is `null`. + * @property {number} [drawingDelay] + */ + /** + * Update e.g. the scale and/or rotation of the page. + * @param {PDFPageViewUpdateParameters} params + */ + update({ scale, rotation, optionalContentConfigPromise, drawingDelay, }: { + /** + * The new scale, if specified. + */ + scale?: number | undefined; + /** + * The new rotation, if specified. + */ + rotation?: number | undefined; + /** + * A promise that is resolved with an {@link OptionalContentConfig}instance. The default value is `null`. + */ + optionalContentConfigPromise?: Promise<OptionalContentConfig> | undefined; + drawingDelay?: number | undefined; + }): void; + /** + * PLEASE NOTE: Most likely you want to use the `this.reset()` method, + * rather than calling this one directly. + */ + cancelRendering({ keepAnnotationLayer, keepAnnotationEditorLayer, keepXfaLayer, keepTextLayer, cancelExtraDelay, }?: { + keepAnnotationLayer?: boolean | undefined; + keepAnnotationEditorLayer?: boolean | undefined; + keepXfaLayer?: boolean | undefined; + keepTextLayer?: boolean | undefined; + cancelExtraDelay?: number | undefined; + }): void; + cssTransform({ target, redrawAnnotationLayer, redrawAnnotationEditorLayer, redrawXfaLayer, redrawTextLayer, hideTextLayer, }: { + target: any; + redrawAnnotationLayer?: boolean | undefined; + redrawAnnotationEditorLayer?: boolean | undefined; + redrawXfaLayer?: boolean | undefined; + redrawTextLayer?: boolean | undefined; + hideTextLayer?: boolean | undefined; + }): void; + get width(): number; + get height(): number; + getPagePoint(x: any, y: any): any[]; + draw(): Promise<any>; + canvas: HTMLCanvasElement | undefined; + outputScale: OutputScale | undefined; + /** + * @param {string|null} label + */ + setPageLabel(label: string | null): void; + /** + * For use by the `PDFThumbnailView.setImage`-method. + * @ignore + */ + get thumbnailCanvas(): HTMLCanvasElement | null | undefined; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_rendering_queue.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_rendering_queue.d.ts new file mode 100644 index 0000000000..fec2974927 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_rendering_queue.d.ts @@ -0,0 +1,50 @@ +/** + * Controls rendering of the views for pages and thumbnails. + */ +declare class PDFRenderingQueue { + pdfViewer: PDFViewer | null; + pdfThumbnailViewer: PDFThumbnailViewer | null; + onIdle: any; + highestPriorityPage: string | null; + /** @type {number} */ + idleTimeout: number; + printing: boolean; + isThumbnailViewEnabled: boolean; + /** + * @param {PDFViewer} pdfViewer + */ + setViewer(pdfViewer: PDFViewer): void; + /** + * @param {PDFThumbnailViewer} pdfThumbnailViewer + */ + setThumbnailViewer(pdfThumbnailViewer: PDFThumbnailViewer): void; + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isHighestPriority(view: IRenderableView): boolean; + /** + * @param {Object} currentlyVisiblePages + */ + renderHighestPriority(currentlyVisiblePages: Object): void; + /** + * @param {Object} visible + * @param {Array} views + * @param {boolean} scrolledDown + * @param {boolean} [preRenderExtra] + */ + getHighestPriority(visible: Object, views: any[], scrolledDown: boolean, preRenderExtra?: boolean | undefined): any; + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isViewFinished(view: IRenderableView): boolean; + /** + * Render a page or thumbnail view. This calls the appropriate function + * based on the views state. If the view is already rendered it will return + * `false`. + * + * @param {IRenderableView} view + */ + renderView(view: IRenderableView): boolean; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.component.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.component.d.ts new file mode 100644 index 0000000000..f30c2cb8c1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.component.d.ts @@ -0,0 +1,3 @@ +declare class PDFScriptingManagerComponents extends PDFScriptingManager { + constructor(options: any); +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.d.ts new file mode 100644 index 0000000000..f9f41c8c7c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_scripting_manager.d.ts @@ -0,0 +1,55 @@ +declare type PDFScriptingManagerOptions = { + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The path and filename of the + * scripting bundle. + */ + sandboxBundleSrc?: string | undefined; + /** + * - The factory that is used when + * initializing scripting; must contain a `createScripting` method. + * PLEASE NOTE: Primarily intended for the default viewer use-case. + */ + externalServices?: Object | undefined; + /** + * - The function that is used to lookup + * the necessary document properties. + */ + docProperties?: Function | undefined; +}; +/** + * @typedef {Object} PDFScriptingManagerOptions + * @property {EventBus} eventBus - The application event bus. + * @property {string} [sandboxBundleSrc] - The path and filename of the + * scripting bundle. + * @property {Object} [externalServices] - The factory that is used when + * initializing scripting; must contain a `createScripting` method. + * PLEASE NOTE: Primarily intended for the default viewer use-case. + * @property {function} [docProperties] - The function that is used to lookup + * the necessary document properties. + */ +declare class PDFScriptingManager { + /** + * @param {PDFScriptingManagerOptions} options + */ + constructor({ eventBus, externalServices, docProperties }: PDFScriptingManagerOptions); + setViewer(pdfViewer: any): void; + setDocument(pdfDocument: any): Promise<void>; + dispatchWillSave(): Promise<any>; + dispatchDidSave(): Promise<any>; + dispatchWillPrint(): Promise<void>; + dispatchDidPrint(): Promise<any>; + get destroyPromise(): any; + get ready(): boolean; + /** + * @private + */ + private get _pageOpenPending(); + /** + * @private + */ + private get _visitedPages(); +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_single_page_viewer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_single_page_viewer.d.ts new file mode 100644 index 0000000000..5a16d8b788 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_single_page_viewer.d.ts @@ -0,0 +1,6 @@ +declare class PDFSinglePageViewer extends PDFViewer { + set scrollMode(mode: any); + _updateScrollMode(): void; + set spreadMode(mode: any); + _updateSpreadMode(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_view.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_view.d.ts new file mode 100644 index 0000000000..783f3c3484 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_view.d.ts @@ -0,0 +1,113 @@ +declare type PDFThumbnailViewOptions = { + /** + * - The viewer element. + */ + container: HTMLDivElement; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The thumbnail's unique ID (normally its number). + */ + id: number; + /** + * - The page viewport. + */ + defaultViewport: PageViewport; + /** + * - + * A promise that is resolved with an {@link OptionalContentConfig} instance. + * The default value is `null`. + */ + optionalContentConfigPromise?: Promise<OptionalContentConfig> | undefined; + /** + * - The navigation/linking service. + */ + linkService: IPDFLinkService; + /** + * - The rendering queue object. + */ + renderingQueue: PDFRenderingQueue; + /** + * - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + */ + pageColors?: Object | undefined; + /** + * - Enables hardware acceleration for + * rendering. The default value is `false`. + */ + enableHWA?: boolean | undefined; +}; +/** + * @implements {IRenderableView} + */ +declare class PDFThumbnailView implements IRenderableView { + /** + * @param {PDFThumbnailViewOptions} options + */ + constructor({ container, eventBus, id, defaultViewport, optionalContentConfigPromise, linkService, renderingQueue, pageColors, enableHWA, }: PDFThumbnailViewOptions); + id: number; + renderingId: string; + pageLabel: string | null; + pdfPage: any; + rotation: number; + viewport: PageViewport; + pdfPageRotate: number; + _optionalContentConfigPromise: Promise<OptionalContentConfig> | null; + pageColors: Object | null; + enableHWA: boolean; + eventBus: EventBus; + linkService: IPDFLinkService; + renderingQueue: PDFRenderingQueue; + renderTask: any; + renderingState: number; + resume: (() => void) | null; + anchor: HTMLAnchorElement; + div: HTMLDivElement; + _placeholderImg: HTMLDivElement; + canvasWidth: number | undefined; + canvasHeight: number | undefined; + scale: number | undefined; + setPdfPage(pdfPage: any): void; + reset(): void; + update({ rotation }: { + rotation?: null | undefined; + }): void; + /** + * PLEASE NOTE: Most likely you want to use the `this.reset()` method, + * rather than calling this one directly. + */ + cancelRendering(): void; + image: HTMLImageElement | undefined; + draw(): Promise<any>; + setImage(pageView: any): void; + /** + * @param {string|null} label + */ + setPageLabel(label: string | null): void; +} +/** + * @typedef {Object} PDFThumbnailViewOptions + * @property {HTMLDivElement} container - The viewer element. + * @property {EventBus} eventBus - The application event bus. + * @property {number} id - The thumbnail's unique ID (normally its number). + * @property {PageViewport} defaultViewport - The page viewport. + * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] - + * A promise that is resolved with an {@link OptionalContentConfig} instance. + * The default value is `null`. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @property {Object} [pageColors] - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + * @property {boolean} [enableHWA] - Enables hardware acceleration for + * rendering. The default value is `false`. + */ +declare class TempImageFactory { + static "__#72@#tempCanvas": null; + static getCanvas(width: any, height: any): (HTMLCanvasElement | CanvasRenderingContext2D | null)[]; + static destroyCanvas(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_viewer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_viewer.d.ts new file mode 100644 index 0000000000..4bd84fba0d --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_thumbnail_viewer.d.ts @@ -0,0 +1,91 @@ +declare type PDFThumbnailViewerOptions = { + /** + * - The container for the thumbnail + * elements. + */ + container: HTMLDivElement; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The navigation/linking service. + */ + linkService: IPDFLinkService; + /** + * - The rendering queue object. + */ + renderingQueue: PDFRenderingQueue; + /** + * - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + */ + pageColors?: Object | undefined; + /** + * - The AbortSignal for the window + * events. + */ + abortSignal?: AbortSignal | undefined; + /** + * - Enables hardware acceleration for + * rendering. The default value is `false`. + */ + enableHWA?: boolean | undefined; +}; +/** + * @typedef {Object} PDFThumbnailViewerOptions + * @property {HTMLDivElement} container - The container for the thumbnail + * elements. + * @property {EventBus} eventBus - The application event bus. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @property {Object} [pageColors] - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + * @property {AbortSignal} [abortSignal] - The AbortSignal for the window + * events. + * @property {boolean} [enableHWA] - Enables hardware acceleration for + * rendering. The default value is `false`. + */ +/** + * Viewer control to display thumbnails for pages in a PDF document. + */ +declare class PDFThumbnailViewer { + /** + * @param {PDFThumbnailViewerOptions} options + */ + constructor({ container, eventBus, linkService, renderingQueue, pageColors, abortSignal, enableHWA, }: PDFThumbnailViewerOptions); + container: HTMLDivElement; + eventBus: EventBus; + linkService: IPDFLinkService; + renderingQueue: PDFRenderingQueue; + pageColors: Object | null; + enableHWA: boolean; + scroll: { + right: boolean; + down: boolean; + lastX: any; + lastY: any; + _eventHandler: (evt: any) => void; + }; + getThumbnail(index: any): any; + scrollThumbnailIntoView(pageNumber: any): void; + _currentPageNumber: any; + set pagesRotation(rotation: any); + get pagesRotation(): any; + _pagesRotation: any; + cleanup(): void; + _thumbnails: any[] | undefined; + _pageLabels: any[] | null | undefined; + /** + * @param {PDFDocumentProxy} pdfDocument + */ + setDocument(pdfDocument: PDFDocumentProxy): void; + pdfDocument: PDFDocumentProxy | undefined; + /** + * @param {Array|null} labels + */ + setPageLabels(labels: any[] | null): void; + forceRendering(): boolean; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_viewer.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_viewer.d.ts new file mode 100644 index 0000000000..58a6a72fde --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/pdf_viewer.d.ts @@ -0,0 +1,500 @@ +declare type PDFViewerOptions = { + /** + * - The container for the viewer element. + */ + container: HTMLDivElement; + /** + * - The viewer element. + */ + viewer?: HTMLDivElement | undefined; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The navigation/linking service. + */ + linkService?: IPDFLinkService | undefined; + /** + * - The download manager + * component. + */ + downloadManager?: IDownloadManager | undefined; + /** + * - The find controller + * component. + */ + findController?: PDFFindController | undefined; + /** + * - The scripting manager + * component. + */ + scriptingManager?: PDFScriptingManager | undefined; + /** + * - The rendering queue object. + */ + renderingQueue?: PDFRenderingQueue | undefined; + /** + * - Removes the border shadow around + * the pages. The default value is `false`. + */ + removePageBorders?: boolean | undefined; + /** + * - Controls if the text layer used for + * selection and searching is created. The constants from {TextLayerMode} + * should be used. The default value is `TextLayerMode.ENABLE`. + */ + textLayerMode?: number | undefined; + /** + * - Controls if the annotation layer is + * created, and if interactive form elements or `AnnotationStorage`-data are + * being rendered. The constants from {@link AnnotationMode} should be used; + * see also {@link RenderParameters} and {@link GetOperatorListParameters}. + * The default value is `AnnotationMode.ENABLE_FORMS`. + */ + annotationMode?: number | undefined; + /** + * - Enables the creation and editing + * of new Annotations. The constants from {@link AnnotationEditorType} should + * be used. The default value is `AnnotationEditorType.NONE`. + */ + annotationEditorMode?: number | undefined; + /** + * - A comma separated list + * of colors to propose to highlight some text in the pdf. + */ + annotationEditorHighlightColors?: string | undefined; + /** + * - Path for image resources, mainly + * mainly for annotation icons. Include trailing slash. + */ + imageResourcesPath?: string | undefined; + /** + * - Enables automatic rotation of + * landscape pages upon printing. The default is `false`. + */ + enablePrintAutoRotate?: boolean | undefined; + /** + * - The maximum supported canvas size in + * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for + * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + */ + maxCanvasPixels?: number | undefined; + /** + * - Localization service. + */ + l10n?: IL10n | undefined; + /** + * - Enables PDF document permissions, + * when they exist. The default value is `false`. + */ + enablePermissions?: boolean | undefined; + /** + * - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + */ + pageColors?: Object | undefined; + /** + * - Enables hardware acceleration for + * rendering. The default value is `false`. + */ + enableHWA?: boolean | undefined; +}; +declare namespace PagesCountLimit { + let FORCE_SCROLL_MODE_PAGE: number; + let FORCE_LAZY_PAGE_INIT: number; + let PAUSE_EAGER_PAGE_INIT: number; +} +/** + * @typedef {Object} PDFViewerOptions + * @property {HTMLDivElement} container - The container for the viewer element. + * @property {HTMLDivElement} [viewer] - The viewer element. + * @property {EventBus} eventBus - The application event bus. + * @property {IPDFLinkService} [linkService] - The navigation/linking service. + * @property {IDownloadManager} [downloadManager] - The download manager + * component. + * @property {PDFFindController} [findController] - The find controller + * component. + * @property {PDFScriptingManager} [scriptingManager] - The scripting manager + * component. + * @property {PDFRenderingQueue} [renderingQueue] - The rendering queue object. + * @property {boolean} [removePageBorders] - Removes the border shadow around + * the pages. The default value is `false`. + * @property {number} [textLayerMode] - Controls if the text layer used for + * selection and searching is created. The constants from {TextLayerMode} + * should be used. The default value is `TextLayerMode.ENABLE`. + * @property {number} [annotationMode] - Controls if the annotation layer is + * created, and if interactive form elements or `AnnotationStorage`-data are + * being rendered. The constants from {@link AnnotationMode} should be used; + * see also {@link RenderParameters} and {@link GetOperatorListParameters}. + * The default value is `AnnotationMode.ENABLE_FORMS`. + * @property {number} [annotationEditorMode] - Enables the creation and editing + * of new Annotations. The constants from {@link AnnotationEditorType} should + * be used. The default value is `AnnotationEditorType.NONE`. + * @property {string} [annotationEditorHighlightColors] - A comma separated list + * of colors to propose to highlight some text in the pdf. + * @property {string} [imageResourcesPath] - Path for image resources, mainly + * mainly for annotation icons. Include trailing slash. + * @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of + * landscape pages upon printing. The default is `false`. + * @property {number} [maxCanvasPixels] - The maximum supported canvas size in + * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for + * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + * @property {IL10n} [l10n] - Localization service. + * @property {boolean} [enablePermissions] - Enables PDF document permissions, + * when they exist. The default value is `false`. + * @property {Object} [pageColors] - Overwrites background and foreground colors + * with user defined ones in order to improve readability in high contrast + * mode. + * @property {boolean} [enableHWA] - Enables hardware acceleration for + * rendering. The default value is `false`. + */ +declare class PDFPageViewBuffer { + constructor(size: any); + push(view: any): void; + /** + * After calling resize, the size of the buffer will be `newSize`. + * The optional parameter `idsToKeep` is, if present, a Set of page-ids to + * push to the back of the buffer, delaying their destruction. The size of + * `idsToKeep` has no impact on the final size of the buffer; if `idsToKeep` + * is larger than `newSize`, some of those pages will be destroyed anyway. + */ + resize(newSize: any, idsToKeep?: null): void; + has(view: any): boolean; + [Symbol.iterator](): SetIterator<any>; +} +/** + * Simple viewer control to display PDF content/pages. + */ +declare class PDFViewer { + /** + * @param {PDFViewerOptions} options + */ + constructor(options: PDFViewerOptions); + container: HTMLDivElement; + viewer: Element | null; + eventBus: EventBus; + linkService: IPDFLinkService | SimpleLinkService; + downloadManager: IDownloadManager | null; + findController: PDFFindController | null; + _scriptingManager: PDFScriptingManager | null; + imageResourcesPath: string; + enablePrintAutoRotate: boolean; + removePageBorders: boolean | undefined; + maxCanvasPixels: number | undefined; + l10n: IL10n | GenericL10n | undefined; + pageColors: Object | null; + defaultRenderingQueue: boolean; + renderingQueue: PDFRenderingQueue | undefined; + scroll: { + right: boolean; + down: boolean; + lastX: any; + lastY: any; + _eventHandler: (evt: any) => void; + }; + presentationModeState: number; + get pagesCount(): number; + getPageView(index: any): any; + getCachedPageViews(): Set<any>; + /** + * @type {boolean} - True if all {PDFPageView} objects are initialized. + */ + get pageViewsReady(): boolean; + /** + * @type {boolean} + */ + get renderForms(): boolean; + /** + * @type {boolean} + */ + get enableScripting(): boolean; + /** + * @param {number} val - The page number. + */ + set currentPageNumber(val: number); + /** + * @type {number} + */ + get currentPageNumber(): number; + /** + * @returns {boolean} Whether the pageNumber is valid (within bounds). + * @private + */ + private _setCurrentPageNumber; + _currentPageNumber: any; + /** + * @param {string} val - The page label. + */ + set currentPageLabel(val: string); + /** + * @type {string|null} Returns the current page label, or `null` if no page + * labels exist. + */ + get currentPageLabel(): string | null; + /** + * @param {number} val - Scale of the pages in percents. + */ + set currentScale(val: number); + /** + * @type {number} + */ + get currentScale(): number; + /** + * @param val - The scale of the pages (in percent or predefined value). + */ + set currentScaleValue(val: string); + /** + * @type {string} + */ + get currentScaleValue(): string; + /** + * @param {number} rotation - The rotation of the pages (0, 90, 180, 270). + */ + set pagesRotation(rotation: number); + /** + * @type {number} + */ + get pagesRotation(): number; + _pagesRotation: any; + get firstPagePromise(): any; + get onePageRendered(): any; + get pagesPromise(): any; + get _layerProperties(): any; + getAllText(): Promise<string | null>; + /** + * @param {PDFDocumentProxy} pdfDocument + */ + setDocument(pdfDocument: PDFDocumentProxy): void; + pdfDocument: PDFDocumentProxy | undefined; + _scrollMode: any; + _optionalContentConfigPromise: Promise<OptionalContentConfig> | null | undefined; + /** + * @param {Array|null} labels + */ + setPageLabels(labels: any[] | null): void; + _pageLabels: any[] | null | undefined; + _resetView(): void; + _pages: any[] | undefined; + _currentScale: any; + _currentScaleValue: any; + _location: { + pageNumber: any; + scale: any; + top: number; + left: number; + rotation: any; + pdfOpenParams: string; + } | null | undefined; + _firstPageCapability: any; + _onePageRenderedCapability: any; + _pagesCapability: any; + _previousScrollMode: any; + _spreadMode: any; + _scrollUpdate(): void; + /** + * @param {string} label - The page label. + * @returns {number|null} The page number corresponding to the page label, + * or `null` when no page labels exist and/or the input is invalid. + */ + pageLabelToPageNumber(label: string): number | null; + /** + * @typedef {Object} ScrollPageIntoViewParameters + * @property {number} pageNumber - The page number. + * @property {Array} [destArray] - The original PDF destination array, in the + * format: <page-ref> </XYZ|/FitXXX> <args..> + * @property {boolean} [allowNegativeOffset] - Allow negative page offsets. + * The default value is `false`. + * @property {boolean} [ignoreDestinationZoom] - Ignore the zoom argument in + * the destination array. The default value is `false`. + */ + /** + * Scrolls page into view. + * @param {ScrollPageIntoViewParameters} params + */ + scrollPageIntoView({ pageNumber, destArray, allowNegativeOffset, ignoreDestinationZoom, }: { + /** + * - The page number. + */ + pageNumber: number; + /** + * - The original PDF destination array, in the + * format: <page-ref> </XYZ|/FitXXX> <args..> + */ + destArray?: any[] | undefined; + /** + * - Allow negative page offsets. + * The default value is `false`. + */ + allowNegativeOffset?: boolean | undefined; + /** + * - Ignore the zoom argument in + * the destination array. The default value is `false`. + */ + ignoreDestinationZoom?: boolean | undefined; + }): void; + _updateLocation(firstPage: any): void; + update(): void; + containsElement(element: any): boolean; + focus(): void; + get _isContainerRtl(): boolean; + get isInPresentationMode(): boolean; + get isChangingPresentationMode(): boolean; + get isHorizontalScrollbarEnabled(): boolean; + get isVerticalScrollbarEnabled(): boolean; + _getVisiblePages(): Object; + cleanup(): void; + /** + * @private + */ + private _cancelRendering; + forceRendering(currentlyVisiblePages: any): boolean; + /** + * @type {boolean} Whether all pages of the PDF document have identical + * widths and heights. + */ + get hasEqualPageSizes(): boolean; + /** + * Returns sizes of the pages. + * @returns {Array} Array of objects with width/height/rotation fields. + */ + getPagesOverview(): any[]; + /** + * @param {Promise<OptionalContentConfig>} promise - A promise that is + * resolved with an {@link OptionalContentConfig} instance. + */ + set optionalContentConfigPromise(promise: Promise<OptionalContentConfig>); + /** + * @type {Promise<OptionalContentConfig | null>} + */ + get optionalContentConfigPromise(): Promise<OptionalContentConfig | null>; + /** + * @param {number} mode - The direction in which the document pages should be + * laid out within the scrolling container. + * The constants from {ScrollMode} should be used. + */ + set scrollMode(mode: number); + /** + * @type {number} One of the values in {ScrollMode}. + */ + get scrollMode(): number; + _updateScrollMode(pageNumber?: null): void; + /** + * @param {number} mode - Group the pages in spreads, starting with odd- or + * even-number pages (unless `SpreadMode.NONE` is used). + * The constants from {SpreadMode} should be used. + */ + set spreadMode(mode: number); + /** + * @type {number} One of the values in {SpreadMode}. + */ + get spreadMode(): number; + _updateSpreadMode(pageNumber?: null): void; + /** + * @private + */ + private _getPageAdvance; + /** + * Go to the next page, taking scroll/spread-modes into account. + * @returns {boolean} Whether navigation occurred. + */ + nextPage(): boolean; + /** + * Go to the previous page, taking scroll/spread-modes into account. + * @returns {boolean} Whether navigation occurred. + */ + previousPage(): boolean; + /** + * @typedef {Object} ChangeScaleOptions + * @property {number} [drawingDelay] + * @property {number} [scaleFactor] + * @property {number} [steps] + * @property {Array} [origin] x and y coordinates of the scale + * transformation origin. + */ + /** + * Changes the current zoom level by the specified amount. + * @param {ChangeScaleOptions} [options] + */ + updateScale({ drawingDelay, scaleFactor, steps, origin }?: { + drawingDelay?: number | undefined; + scaleFactor?: number | undefined; + steps?: number | undefined; + /** + * x and y coordinates of the scale + * transformation origin. + */ + origin?: any[] | undefined; + } | undefined): void; + /** + * Increase the current zoom level one, or more, times. + * @param {ChangeScaleOptions} [options] + */ + increaseScale(options?: { + drawingDelay?: number | undefined; + scaleFactor?: number | undefined; + steps?: number | undefined; + /** + * x and y coordinates of the scale + * transformation origin. + */ + origin?: any[] | undefined; + } | undefined): void; + /** + * Decrease the current zoom level one, or more, times. + * @param {ChangeScaleOptions} [options] + */ + decreaseScale(options?: { + drawingDelay?: number | undefined; + scaleFactor?: number | undefined; + steps?: number | undefined; + /** + * x and y coordinates of the scale + * transformation origin. + */ + origin?: any[] | undefined; + } | undefined): void; + get containerTopLeft(): number[]; + /** + * @typedef {Object} AnnotationEditorModeOptions + * @property {number} mode - The editor mode (none, FreeText, ink, ...). + * @property {string|null} [editId] - ID of the existing annotation to edit. + * @property {boolean} [isFromKeyboard] - True if the mode change is due to a + * keyboard action. + */ + /** + * @param {AnnotationEditorModeOptions} options + */ + set annotationEditorMode({ mode, editId, isFromKeyboard }: { + /** + * - The editor mode (none, FreeText, ink, ...). + */ + mode: number; + /** + * - ID of the existing annotation to edit. + */ + editId?: string | null | undefined; + /** + * - True if the mode change is due to a + * keyboard action. + */ + isFromKeyboard?: boolean | undefined; + }); + get annotationEditorMode(): { + /** + * - The editor mode (none, FreeText, ink, ...). + */ + mode: number; + /** + * - ID of the existing annotation to edit. + */ + editId?: string | null | undefined; + /** + * - True if the mode change is due to a + * keyboard action. + */ + isFromKeyboard?: boolean | undefined; + }; + refresh(noUpdate?: boolean, updateArgs?: any): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/struct_tree_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/struct_tree_layer_builder.d.ts new file mode 100644 index 0000000000..48aa7168f3 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/struct_tree_layer_builder.d.ts @@ -0,0 +1,8 @@ +declare class StructTreeLayerBuilder { + constructor(pdfPage: any, rawDims: any); + render(): Promise<any>; + getAriaAttributes(annotationId: any): Promise<any>; + hide(): void; + show(): void; + addElementsToTextLayer(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_accessibility.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_accessibility.d.ts new file mode 100644 index 0000000000..2685aba5c3 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_accessibility.d.ts @@ -0,0 +1,43 @@ +/** + * This class aims to provide some methods: + * - to reorder elements in the DOM with respect to the visual order; + * - to create a link, using aria-owns, between spans in the textLayer and + * annotations in the annotationLayer. The goal is to help to know + * where the annotations are in the text flow. + */ +declare class TextAccessibilityManager { + /** + * Compare the positions of two elements, it must correspond to + * the visual ordering. + * + * @param {HTMLElement} e1 + * @param {HTMLElement} e2 + * @returns {number} + */ + static "__#7@#compareElementPositions"(e1: HTMLElement, e2: HTMLElement): number; + setTextMapping(textDivs: any): void; + /** + * Function called when the text layer has finished rendering. + */ + enable(): void; + disable(): void; + /** + * Remove an aria-owns id from a node in the text layer. + * @param {HTMLElement} element + */ + removePointerInTextLayer(element: HTMLElement): void; + /** + * Find the text node which is the nearest and add an aria-owns attribute + * in order to correctly position this editor in the text flow. + * @param {HTMLElement} element + * @param {boolean} isRemovable + * @returns {string|null} The id in the struct tree if any. + */ + addPointerInTextLayer(element: HTMLElement, isRemovable: boolean): string | null; + /** + * Move a div in the DOM in order to respect the visual order. + * @param {HTMLDivElement} element + * @returns {string|null} The id in the struct tree if any. + */ + moveElementInDOM(container: any, element: HTMLDivElement, contentElement: any, isRemovable: any): string | null; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_highlighter.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_highlighter.d.ts new file mode 100644 index 0000000000..d482445d4d --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_highlighter.d.ts @@ -0,0 +1,60 @@ +declare type TextHighlighterOptions = { + findController: PDFFindController; + /** + * - The application event bus. + */ + eventBus: EventBus; + /** + * - The page index. + */ + pageIndex: number; +}; +/** @typedef {EventBus} EventBus */ +/** @typedef {PDFFindController} PDFFindController */ +/** + * @typedef {Object} TextHighlighterOptions + * @property {PDFFindController} findController + * @property {EventBus} eventBus - The application event bus. + * @property {number} pageIndex - The page index. + */ +/** + * TextHighlighter handles highlighting matches from the FindController in + * either the text layer or XFA layer depending on the type of document. + */ +declare class TextHighlighter { + /** + * @param {TextHighlighterOptions} options + */ + constructor({ findController, eventBus, pageIndex }: TextHighlighterOptions); + findController: PDFFindController; + matches: any[]; + eventBus: EventBus; + pageIdx: number; + textDivs: Node[] | null; + textContentItemsStr: string[] | null; + enabled: boolean; + /** + * Store two arrays that will map DOM nodes to text they should contain. + * The arrays should be of equal length and the array element at each index + * should correspond to the other. e.g. + * `items[0] = "<span>Item 0</span>" and texts[0] = "Item 0"; + * + * @param {Array<Node>} divs + * @param {Array<string>} texts + */ + setTextMapping(divs: Array<Node>, texts: Array<string>): void; + /** + * Start listening for events to update the highlighter and check if there are + * any current matches that need be highlighted. + */ + enable(): void; + disable(): void; + _convertMatches(matches: any, matchesLength: any): { + begin: { + divIdx: number; + offset: number; + }; + }[]; + _renderMatches(matches: any): void; + _updateMatches(reset?: boolean): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_layer_builder.d.ts new file mode 100644 index 0000000000..6110b08a20 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/text_layer_builder.d.ts @@ -0,0 +1,52 @@ +declare type TextLayerBuilderOptions = { + pdfPage: PDFPageProxy; + /** + * - Optional object that will handle + * highlighting text from the find controller. + */ + highlighter?: TextHighlighter | undefined; + accessibilityManager?: TextAccessibilityManager | undefined; + onAppend?: Function | undefined; +}; +/** + * @typedef {Object} TextLayerBuilderOptions + * @property {PDFPageProxy} pdfPage + * @property {TextHighlighter} [highlighter] - Optional object that will handle + * highlighting text from the find controller. + * @property {TextAccessibilityManager} [accessibilityManager] + * @property {function} [onAppend] + */ +/** + * The text layer builder provides text selection functionality for the PDF. + * It does this by creating overlay divs over the PDF's text. These divs + * contain text that matches the PDF text they are overlaying. + */ +declare class TextLayerBuilder { + static "__#68@#textLayers": Map<any, any>; + static "__#68@#selectionChangeAbortController": null; + static "__#68@#removeGlobalSelectionListener"(textLayerDiv: any): void; + static "__#68@#enableGlobalSelectionListener"(): void; + constructor({ pdfPage, highlighter, accessibilityManager, enablePermissions, onAppend, }: { + pdfPage: any; + highlighter?: null | undefined; + accessibilityManager?: null | undefined; + enablePermissions?: boolean | undefined; + onAppend?: null | undefined; + }); + pdfPage: any; + highlighter: any; + accessibilityManager: any; + div: HTMLDivElement; + /** + * Renders the text layer. + * @param {PageViewport} viewport + * @param {Object} [textContentParams] + */ + render(viewport: PageViewport, textContentParams?: Object | undefined): Promise<void>; + hide(): void; + show(): void; + /** + * Cancel rendering of the text layer. + */ + cancel(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/ui_utils.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/ui_utils.d.ts new file mode 100644 index 0000000000..0608be57c2 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/ui_utils.d.ts @@ -0,0 +1,272 @@ +declare type GetPageSizeInchesParameters = { + view: number[]; + userUnit: number; + rotate: number; +}; +declare type PageSize = { + /** + * - In inches. + */ + width: number; + /** + * - In inches. + */ + height: number; +}; +declare type GetVisibleElementsParameters = { + /** + * - A container that can possibly scroll. + */ + scrollEl: HTMLElement; + /** + * - Objects with a `div` property that contains an + * HTMLElement, which should all be descendants of `scrollEl` satisfying the + * relevant layout assumptions. + */ + views: any[]; + /** + * - If `true`, the returned elements are + * sorted in descending order of the percent of their padding box that is + * visible. The default value is `false`. + */ + sortByVisibility: boolean; + /** + * - If `true`, the elements are assumed to be + * laid out horizontally instead of vertically. The default value is `false`. + */ + horizontal: boolean; + /** + * - If `true`, the `scrollEl` container is assumed to + * be in right-to-left mode. The default value is `false`. + */ + rtl: boolean; +}; +/** + * Promise that is resolved when DOM window becomes visible. + */ +declare const animationStarted: Promise<any>; +/** + * Converts API PageLayout values to the format used by `BaseViewer`. + * @param {string} layout - The API PageLayout value. + * @returns {Object} + */ +declare function apiPageLayoutToViewerModes(layout: string): Object; +/** + * Converts API PageMode values to the format used by `PDFSidebar`. + * NOTE: There's also a "FullScreen" parameter which is not possible to support, + * since the Fullscreen API used in browsers requires that entering + * fullscreen mode only occurs as a result of a user-initiated event. + * @param {string} mode - The API PageMode value. + * @returns {number} A value from {SidebarView}. + */ +declare function apiPageModeToSidebarView(mode: string): number; +/** + * Approximates float number as a fraction using Farey sequence (max order + * of 8). + * @param {number} x - Positive float number. + * @returns {Array} Estimated fraction: the first array item is a numerator, + * the second one is a denominator. + * They are both natural numbers. + */ +declare function approximateFraction(x: number): any[]; +declare const AutoPrintRegExp: RegExp; +/** + * Helper function for getVisibleElements. + * + * @param {number} index - initial guess at the first visible element + * @param {Array} views - array of pages, into which `index` is an index + * @param {number} top - the top of the scroll pane + * @returns {number} less than or equal to `index` that is definitely at or + * before the first visible element in `views`, but not by too much. (Usually, + * this will be the first element in the first partially visible row in + * `views`, although sometimes it goes back one row further.) + */ +declare function backtrackBeforeAllVisibleElements(index: number, views: any[], top: number): number; +/** + * Use binary search to find the index of the first item in a given array which + * passes a given condition. The items are expected to be sorted in the sense + * that if the condition is true for one item in the array, then it is also true + * for all following items. + * + * @returns {number} Index of the first array element to pass the test, + * or |items.length| if no such element exists. + */ +declare function binarySearchFirstItem(items: any, condition: any, start?: number): number; +declare const calcRound: ((x: number) => number) | ((x: any) => any); +declare namespace CursorTool { + let SELECT: number; + let HAND: number; + let ZOOM: number; +} +declare const DEFAULT_SCALE: 1; +declare const DEFAULT_SCALE_DELTA: 1.1; +declare const DEFAULT_SCALE_VALUE: "auto"; +declare const docStyle: CSSStyleDeclaration | null; +/** + * @param {number} x - A positive number to round to a multiple of `div`. + * @param {number} div - A natural number. + */ +declare function floorToDivide(x: number, div: number): number; +/** + * Get the active or focused element in current DOM. + * + * Recursively search for the truly active or focused element in case there are + * shadow DOMs. + * + * @returns {Element} the truly active or focused element. + */ +declare function getActiveOrFocusedElement(): Element; +/** + * @typedef {Object} GetPageSizeInchesParameters + * @property {number[]} view + * @property {number} userUnit + * @property {number} rotate + */ +/** + * @typedef {Object} PageSize + * @property {number} width - In inches. + * @property {number} height - In inches. + */ +/** + * Gets the size of the specified page, converted from PDF units to inches. + * @param {GetPageSizeInchesParameters} params + * @returns {PageSize} + */ +declare function getPageSizeInches({ view, userUnit, rotate }: GetPageSizeInchesParameters): PageSize; +/** + * @typedef {Object} GetVisibleElementsParameters + * @property {HTMLElement} scrollEl - A container that can possibly scroll. + * @property {Array} views - Objects with a `div` property that contains an + * HTMLElement, which should all be descendants of `scrollEl` satisfying the + * relevant layout assumptions. + * @property {boolean} sortByVisibility - If `true`, the returned elements are + * sorted in descending order of the percent of their padding box that is + * visible. The default value is `false`. + * @property {boolean} horizontal - If `true`, the elements are assumed to be + * laid out horizontally instead of vertically. The default value is `false`. + * @property {boolean} rtl - If `true`, the `scrollEl` container is assumed to + * be in right-to-left mode. The default value is `false`. + */ +/** + * Generic helper to find out what elements are visible within a scroll pane. + * + * Well, pretty generic. There are some assumptions placed on the elements + * referenced by `views`: + * - If `horizontal`, no left of any earlier element is to the right of the + * left of any later element. + * - Otherwise, `views` can be split into contiguous rows where, within a row, + * no top of any element is below the bottom of any other element, and + * between rows, no bottom of any element in an earlier row is below the + * top of any element in a later row. + * + * (Here, top, left, etc. all refer to the padding edge of the element in + * question. For pages, that ends up being equivalent to the bounding box of the + * rendering canvas. Earlier and later refer to index in `views`, not page + * layout.) + * + * @param {GetVisibleElementsParameters} params + * @returns {Object} `{ first, last, views: [{ id, x, y, view, percent }] }` + */ +declare function getVisibleElements({ scrollEl, views, sortByVisibility, horizontal, rtl, }: GetVisibleElementsParameters): Object; +declare function isPortraitOrientation(size: any): boolean; +declare function isValidRotation(angle: any): boolean; +declare function isValidScrollMode(mode: any): boolean; +declare function isValidSpreadMode(mode: any): boolean; +declare const MAX_AUTO_SCALE: 1.25; +declare const MAX_SCALE: 10; +declare const MIN_SCALE: 0.1; +declare function normalizeWheelEventDelta(evt: any): number; +declare function normalizeWheelEventDirection(evt: any): number; +/** + * Helper function to parse query string (e.g. ?param1=value¶m2=...). + * @param {string} query + * @returns {Map} + */ +declare function parseQueryString(query: string): Map<any, any>; +declare namespace PresentationModeState { + let UNKNOWN: number; + let NORMAL: number; + let CHANGING: number; + let FULLSCREEN: number; +} +declare class ProgressBar { + constructor(bar: any); + set percent(val: number); + get percent(): number; + setWidth(viewer: any): void; + setDisableAutoFetch(delay?: number): void; + hide(): void; + show(): void; +} +/** + * @param {string} str + * @param {boolean} [replaceInvisible] + */ +declare function removeNullCharacters(str: string, replaceInvisible?: boolean | undefined): string; +declare namespace RenderingStates { + let INITIAL: number; + let RUNNING: number; + let PAUSED: number; + let FINISHED: number; +} +declare const SCROLLBAR_PADDING: 40; +/** + * Scrolls specified element into view of its parent. + * @param {HTMLElement} element - The element to be visible. + * @param {Object} [spot] - An object with optional top and left properties, + * specifying the offset from the top left edge. + * @param {number} [spot.left] + * @param {number} [spot.top] + * @param {boolean} [scrollMatches] - When scrolling search results into view, + * ignore elements that either: Contains marked content identifiers, + * or have the CSS-rule `overflow: hidden;` set. The default value is `false`. + */ +declare function scrollIntoView(element: HTMLElement, spot?: { + left?: number | undefined; + top?: number | undefined; +} | undefined, scrollMatches?: boolean | undefined): void; +declare namespace ScrollMode { + let UNKNOWN_1: number; + //export { UNKNOWN_1 as UNKNOWN }; + let VERTICAL: number; + let HORIZONTAL: number; + let WRAPPED: number; + let PAGE: number; +} +declare namespace SidebarView { + let UNKNOWN_2: number; + //export { UNKNOWN_2 as UNKNOWN }; + let NONE: number; + let THUMBS: number; + let OUTLINE: number; + let ATTACHMENTS: number; + let LAYERS: number; +} +declare namespace SpreadMode { + let UNKNOWN_3: number; + //export { UNKNOWN_3 as UNKNOWN }; + let NONE_1: number; + //export { NONE_1 as NONE }; + let ODD: number; + let EVEN: number; +} +declare namespace TextLayerMode { + let DISABLE: number; + let ENABLE: number; + let ENABLE_PERMISSIONS: number; +} +declare function toggleCheckedBtn(button: any, toggle: any, view?: null): void; +declare function toggleExpandedBtn(button: any, toggle: any, view?: null): void; +declare const UNKNOWN_SCALE: 0; +declare const VERTICAL_PADDING: 5; +/** + * Helper function to start monitoring the scroll event and converting them into + * PDF.js friendly one: with scroll debounce and scroll direction. + */ +declare function watchScroll(viewAreaElement: any, callback: any, abortSignal?: undefined): { + right: boolean; + down: boolean; + lastX: any; + lastY: any; + _eventHandler: (evt: any) => void; +}; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/xfa_layer_builder.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/xfa_layer_builder.d.ts new file mode 100644 index 0000000000..40cab5d9f4 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/@types/web/xfa_layer_builder.d.ts @@ -0,0 +1,35 @@ +declare type XfaLayerBuilderOptions = { + pdfPage: PDFPageProxy; + annotationStorage?: AnnotationStorage | undefined; + linkService: IPDFLinkService; + xfaHtml?: Object | undefined; +}; +/** + * @typedef {Object} XfaLayerBuilderOptions + * @property {PDFPageProxy} pdfPage + * @property {AnnotationStorage} [annotationStorage] + * @property {IPDFLinkService} linkService + * @property {Object} [xfaHtml] + */ +declare class XfaLayerBuilder { + /** + * @param {XfaLayerBuilderOptions} options + */ + constructor({ pdfPage, annotationStorage, linkService, xfaHtml, }: XfaLayerBuilderOptions); + pdfPage: PDFPageProxy; + annotationStorage: AnnotationStorage; + linkService: IPDFLinkService; + xfaHtml: Object; + div: HTMLDivElement | null; + _cancelled: boolean; + /** + * @param {PageViewport} viewport + * @param {string} intent (default value is 'display') + * @returns {Promise<Object | void>} A promise that is resolved when rendering + * of the XFA layer is complete. The first rendering will return an object + * with a `textDivs` property that can be used with the TextHighlighter. + */ + render(viewport: PageViewport, intent?: string): Promise<Object | void>; + cancel(): void; + hide(): void; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor new file mode 100644 index 0000000000..5571e213f9 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor @@ -0,0 +1,15 @@ +@namespace Bit.BlazorUI + +<div style="@Style" class="bit-pdr @(Horizontal ? "bit-pdr-hor" : "") @Class"> + @if (RenderAllPages) + { + for (int i = 0; i < _numberOfPages; i++) + { + <canvas @key="@($"{Config.Id}-{i+1}")" id="@($"{Config.Id}-{i+1}")" style="@CanvasStyle" class="@CanvasClass" /> + } + } + else + { + <canvas id="@Config.Id" style="@CanvasStyle" class="@CanvasClass" /> + } +</div> \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs new file mode 100644 index 0000000000..2b55f44bf8 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs @@ -0,0 +1,243 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Bit.BlazorUI; + +public partial class BitPdfReader +{ + private int _currentPageNumber = 1; + private bool _allPageRendered; + private int _numberOfPages = 1; + private bool _parametersInitialized; + + + + [Inject] private IJSRuntime _js { get; set; } + + + + /// <summary> + /// The CSS class of the canvas element(s). + /// </summary> + [Parameter] public string? CanvasClass { get; set; } + + /// <summary> + /// The CSS style of the canvas element(s). + /// </summary> + [Parameter] public string? CanvasStyle { get; set; } + + /// <summary> + /// The CSS class of the root element. + /// </summary> + [Parameter] public string? Class { get; set; } + + /// <summary> + /// The configuration of the pdf reader (<see cref="BitPdfReaderConfig"/>). + /// </summary> + [Parameter] public BitPdfReaderConfig Config { get; set; } + + /// <summary> + /// Renders the pages horizontally. + /// </summary> + [Parameter] public bool Horizontal { get; set; } + + /// <summary> + /// The page number to render initially. + /// </summary> + [Parameter] public int InitialPageNumber { get; set; } = 1; + + /// <summary> + /// The callback for when the pdf page is done rendering. + /// </summary> + [Parameter] public EventCallback OnPdfPageRendered { get; set; } + + /// <summary> + /// The callback for when the pdf document is done loading and processing. + /// </summary> + [Parameter] public EventCallback OnPdfLoaded { get; set; } + + /// <summary> + /// Whether render all pages at start. + /// </summary> + [Parameter] public bool RenderAllPages { get; set; } + + /// <summary> + /// The CSS style of the root element. + /// </summary> + [Parameter] public string? Style { get; set; } + + + /// <summary> + /// Re-renders the provided page number or the current page. + /// </summary> + public ValueTask Refresh(int pageNumber = 0) + { + pageNumber = pageNumber == 0 ? _currentPageNumber : pageNumber; + + if (pageNumber > _numberOfPages) return ValueTask.CompletedTask; + + return RefreshPage(pageNumber); + } + + /// <summary> + /// Re-renders all of the pages. + /// </summary> + public Task RefreshAll() + { + return RefreshAllPages(); + } + + /// <summary> + /// Renders the first page. + /// </summary> + public Task First() + { + return Go(1); + } + + /// <summary> + /// Renders the previous page. + /// </summary> + public Task Prev() + { + return Go(_currentPageNumber - 1); + } + + /// <summary> + /// Renders the next page. + /// </summary> + public Task Next() + { + return Go(_currentPageNumber + 1); + } + + /// <summary> + /// Renders the last page. + /// </summary> + public Task Last() + { + return Go(_numberOfPages); + } + + /// <summary> + /// Renders the provided page number. + /// </summary> + public async Task Go(int pageNumber) + { + if (pageNumber < 1) return; + if (pageNumber > _numberOfPages) return; + + _currentPageNumber = pageNumber; + + if (RenderAllPages) + { + //TODO: implement scroll to the page + } + else + { + await _js.BitPdfReaderRenderPage(Config.Id, _currentPageNumber); + } + } + + /// <summary> + /// The current page number that is currently rendered. + /// </summary> + public int CurrentPageNumber => _currentPageNumber; + + /// <summary> + /// Number of total pages of the current pdf document. + /// </summary> + public int NumberOfPages => _numberOfPages; + + + + protected override Task OnParametersSetAsync() + { + if (_parametersInitialized is false) + { + _currentPageNumber = InitialPageNumber; + _parametersInitialized = true; + } + + return base.OnParametersSetAsync(); + } + + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(BitPdfReaderConfig))] + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + string[] scripts = [ + "_content/Bit.BlazorUI.Extras/pdf.js/pdfjs-4.7.76.js", + "_content/Bit.BlazorUI.Extras/pdf.js/pdfjs-4.7.76-worker.js" + ]; + + await _js.BitPdfReaderInitPdfJs(scripts); + + _numberOfPages = await _js.BitPdfReaderSetupPdfDoc(Config); + + await OnPdfLoaded.InvokeAsync(); + + if (RenderAllPages is false) + { + await Render(InitialPageNumber); + } + + StateHasChanged(); + } + else + { + if (RenderAllPages && _allPageRendered is false) + { + await RenderAll(); + + _allPageRendered = true; + } + } + } + + private async ValueTask Render(int pageNumber) + { + await _js.BitPdfReaderRenderPage(Config.Id, pageNumber); + + await OnPdfPageRendered.InvokeAsync(); + } + + private async Task RenderAll() + { + if (RenderAllPages is false) return; + + List<Task> tasks = []; + + for (int i = 0; i < _numberOfPages; i++) + { + tasks.Add(_js.BitPdfReaderRenderPage(Config.Id, i + 1).AsTask()); + } + + await Task.WhenAll(tasks); + + await OnPdfPageRendered.InvokeAsync(); + } + + private async ValueTask RefreshPage(int pageNumber) + { + await _js.BitPdfReaderRefreshPage(Config, pageNumber); + + await OnPdfPageRendered.InvokeAsync(); + } + + private async Task RefreshAllPages() + { + if (RenderAllPages is false) return; + + List<Task> tasks = []; + + for (int i = 0; i < _numberOfPages; i++) + { + tasks.Add(_js.BitPdfReaderRefreshPage(Config, i + 1).AsTask()); + } + + await Task.WhenAll(tasks); + + await OnPdfPageRendered.InvokeAsync(); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.scss new file mode 100644 index 0000000000..9b24bd0b94 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.scss @@ -0,0 +1,9 @@ +.bit-pdr { + gap: 1rem; + display: flex; + flex-direction: column; +} + +.bit-pdr-hor { + flex-direction: row; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts new file mode 100644 index 0000000000..4e27a2b717 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts @@ -0,0 +1,118 @@ +declare type PdfJsLib = { + getDocument: (src?: string | URL | TypedArray | ArrayBuffer | DocumentInitParameters) => PDFDocumentLoadingTask; + GlobalWorkerOptions: GlobalWorkerOptions +} + +declare type BitPdfReaderConfig = { + id: string; + url: string; + scale: number; + autoScale: boolean; + pdfDoc?: PDFDocumentProxy; + isRendering: boolean[]; +} + +namespace BitBlazorUI { + export class BitPdfReader { + private static _initPromise?: Promise<unknown>; + private static _bitPdfReaders = new Map<string, BitPdfReaderConfig>(); + + public static async init(scripts: string[]) { + if (BitPdfReader._initPromise) { + await BitPdfReader._initPromise; + } + + const allScripts = Array.from(document.scripts).map(s => s.src); + const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); + + if (notAppenedScripts.length == 0) return Promise.resolve(); + + const promise = new Promise(async (resolve: any, reject: any) => { + try { + for (let url of notAppenedScripts) await addScript(url); + resolve(); + } catch (e: any) { + reject(e); + } + }); + BitPdfReader._initPromise = promise; + return promise; + + async function addScript(url: string) { + return new Promise((res, rej) => { + const script = document.createElement('script'); + script.src = url; + script.type = 'module'; + script.onload = res; + script.onerror = rej; + document.body.appendChild(script); + }) + } + } + + public static async setup(config: BitPdfReaderConfig) { + const { pdfjsLib } = globalThis as unknown as { pdfjsLib: PdfJsLib }; + + const loadingTask = pdfjsLib.getDocument(config.url); + const pdfDoc = await loadingTask.promise; + config.pdfDoc = pdfDoc; + config.isRendering = []; + BitPdfReader._bitPdfReaders.set(config.id, config); + + return pdfDoc.numPages; + } + + public static async refreshPage(config: BitPdfReaderConfig, pageNumber: number) { + let oldConfig = BitPdfReader._bitPdfReaders.get(config.id); + if (oldConfig) { + BitPdfReader._bitPdfReaders.set(config.id, Object.assign(oldConfig, config)); + } else { + BitPdfReader.setup(config); + } + + await BitPdfReader.renderPage(config.id, pageNumber); + } + + public static async renderPage(id: string, pageNumber: number) { + const config = BitPdfReader._bitPdfReaders.get(id); + + if (!config || !config.pdfDoc) return; + + if (config.isRendering[pageNumber]) return; + if (pageNumber < 1 || pageNumber > config.pdfDoc.numPages) return; + + config.isRendering[pageNumber] = true; + try { + const page = await config.pdfDoc.getPage(pageNumber); + + const pixelRatio = (config.autoScale && window.devicePixelRatio) || 1; + const scale = config.scale * pixelRatio; + + const viewport = page.getViewport({ scale: scale }); + + let canvas = document.getElementById(config.id) as HTMLCanvasElement; + if (!canvas) { + canvas = document.getElementById(`${config.id}-${pageNumber}`) as HTMLCanvasElement; + } + + const context = canvas.getContext('2d')!; + canvas.width = viewport.width; + canvas.height = viewport.height; + + canvas.style.width = `${viewport.width / pixelRatio}px`; + canvas.style.height = `${viewport.height / pixelRatio}px`; + + const renderContext: RenderParameters = { + canvasContext: context, + viewport: viewport + }; + + const renderTask = page.render(renderContext); + await renderTask.promise; + + } finally { + config.isRendering[pageNumber] = false; + } + } + } +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderConfig.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderConfig.cs new file mode 100644 index 0000000000..081d841907 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderConfig.cs @@ -0,0 +1,24 @@ +namespace Bit.BlazorUI; + +public class BitPdfReaderConfig +{ + /// <summary> + /// The id of the pdf reader instance and its canvas element(s). + /// </summary> + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// <summary> + /// The URL of the pdf file. + /// </summary> + public string Url { get; set; } + + /// <summary> + /// The scale in which the pdf document gets rendered on the page. + /// </summary> + public double Scale { get; set; } = 1; + + /// <summary> + /// Automatically scales the pdf based on the device pixel-ratio. + /// </summary> + public bool AutoScale { get; set; } = true; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsInterop.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsInterop.cs new file mode 100644 index 0000000000..a661b71cea --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsInterop.cs @@ -0,0 +1,24 @@ +namespace Bit.BlazorUI; + +internal static class BitPdfReaderJsInterop +{ + public static ValueTask BitPdfReaderInitPdfJs(this IJSRuntime jsRuntime, IEnumerable<string> scripts) + { + return jsRuntime.InvokeVoidAsync("BitBlazorUI.BitPdfReader.init", scripts); + } + + public static ValueTask<int> BitPdfReaderSetupPdfDoc(this IJSRuntime jsRuntime, BitPdfReaderConfig config) + { + return jsRuntime.InvokeAsync<int>("BitBlazorUI.BitPdfReader.setup", config); + } + + public static ValueTask BitPdfReaderRenderPage(this IJSRuntime jsRuntime, string id, int pageNumber) + { + return jsRuntime.InvokeVoidAsync("BitBlazorUI.BitPdfReader.renderPage", id, pageNumber); + } + + public static ValueTask BitPdfReaderRefreshPage(this IJSRuntime jsRuntime, BitPdfReaderConfig config, int pageNumber) + { + return jsRuntime.InvokeVoidAsync("BitBlazorUI.BitPdfReader.refreshPage", config, pageNumber); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss index f5e8300bf7..66554e8edc 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss @@ -1 +1,2 @@ -@import "components.scss"; +@import "fabric.mdl2.bit.blazoui.extras.scss"; +@import "components.scss"; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss index fa2f1bfe04..0e9f9d3e91 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss @@ -1,2 +1,4 @@ @import "../Components/DataGrid/BitDataGrid.scss"; @import "../Components/DataGrid/Pagination/BitDataGridPaginator.scss"; + +@import "../Components/PdfReader/BitPdfReader.scss"; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/fabric.mdl2.bit.blazoui.extras.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/fabric.mdl2.bit.blazoui.extras.scss new file mode 100644 index 0000000000..e372997a9a --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/fabric.mdl2.bit.blazoui.extras.scss @@ -0,0 +1,7 @@ +@font-face { + font-style: normal; + font-display: swap; + font-weight: normal; + font-family: 'Fabric MDL2 bit BlazorUI Extras'; + src: url('../fonts/FabMDL2.4.66.bit.BlazorUI.Extras.woff2') format("woff2"); +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/compilerconfig.json b/src/BlazorUI/Bit.BlazorUI.Extras/compilerconfig.json index 2c4fd68630..d10ac510a2 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/compilerconfig.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/compilerconfig.json @@ -1,6 +1,6 @@ [ { - "outputFile": "Styles/bit.blazorui.extras.css", + "outputFile": "wwwroot/styles/bit.blazorui.extras.css", "inputFile": "Styles/bit.blazorui.extras.scss", "options": { "relativeUrls": false, diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json index 5faae1106a..aed183cc40 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package.json b/src/BlazorUI/Bit.BlazorUI.Extras/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.Extras.woff2 b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.Extras.woff2 new file mode 100644 index 0000000000..ff70846337 Binary files /dev/null and b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.Extras.woff2 differ diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76-worker.js b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76-worker.js new file mode 100644 index 0000000000..ba391b489b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76-worker.js @@ -0,0 +1,21 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */var e={d:(t,i)=>{for(var a in i)e.o(i,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:i[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},__webpack_exports__ = globalThis.pdfjsWorker = {};e.d(__webpack_exports__,{WorkerMessageHandler:()=>WorkerMessageHandler});const t=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),i=[1,0,0,1,0,0],a=[.001,0,0,.001,0,0],s=1.35,r=.35,n=.25925925925925924,g=1,o=2,c=4,C=8,h=16,l=64,Q=128,E=256,u="pdfjs_internal_editor_",d=3,f=9,p=13,m=15,y={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},w=0,D=4,b=1,F=2,S=3,k=1,R=2,N=3,G=4,x=5,U=6,M=7,L=8,H=9,J=10,Y=11,v=12,K=13,T=14,q=15,O=16,W=17,j=20,X="Group",Z="R",V=1,z=2,_=4,$=16,AA=32,eA=128,tA=512,iA=1,aA=2,sA=4096,rA=8192,nA=32768,gA=65536,oA=131072,IA=1048576,cA=2097152,CA=8388608,hA=16777216,BA=1,lA=2,QA=3,EA=4,uA=5,dA={E:"Mouse Enter",X:"Mouse Exit",D:"Mouse Down",U:"Mouse Up",Fo:"Focus",Bl:"Blur",PO:"PageOpen",PC:"PageClose",PV:"PageVisible",PI:"PageInvisible",K:"Keystroke",F:"Format",V:"Validate",C:"Calculate"},fA={WC:"WillClose",WS:"WillSave",DS:"DidSave",WP:"WillPrint",DP:"DidPrint"},pA={O:"PageOpen",C:"PageClose"},mA={ERRORS:0,WARNINGS:1,INFOS:5},yA={NONE:0,BINARY:1},wA=1,DA=2,bA=3,FA=4,SA=5,kA=6,RA=7,NA=8,GA=9,xA=10,UA=11,MA=12,LA=13,HA=14,JA=15,YA=16,vA=17,KA=18,TA=19,qA=20,OA=21,PA=22,WA=23,jA=24,XA=25,ZA=26,VA=27,zA=28,_A=29,$A=30,Ae=31,ee=32,te=33,ie=34,ae=35,se=36,re=37,ne=38,ge=39,oe=40,Ie=41,ce=42,Ce=43,he=44,Be=45,le=46,Qe=47,Ee=48,ue=49,de=50,fe=51,pe=52,me=53,ye=54,we=55,De=56,be=57,Fe=58,Se=59,ke=60,Re=61,Ne=62,Ge=63,xe=64,Ue=65,Me=66,Le=67,He=68,Je=69,Ye=70,ve=71,Ke=72,Te=73,qe=74,Oe=75,Pe=76,We=77,je=80,Xe=81,Ze=83,Ve=84,ze=85,_e=86,$e=87,At=88,et=89,tt=90,it=91,at=92,st=93,rt=1,nt=2;let gt=mA.WARNINGS;function getVerbosityLevel(){return gt}function info(e){gt>=mA.INFOS&&console.log(`Info: ${e}`)}function warn(e){gt>=mA.WARNINGS&&console.log(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unreachable(t)}function createValidAbsoluteUrl(e,t=null,i=null){if(!e)return null;try{if(i&&"string"==typeof e){if(i.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\./g);t?.length>=2&&(e=`http://${e}`)}if(i.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const a=t?new URL(e,t):new URL(e);if(function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(a))return a}catch{}return null}function shadow(e,t,i,a=!1){Object.defineProperty(e,t,{value:i,enumerable:!a,configurable:!0,writable:!1});return i}const ot=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends ot{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends ot{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extends ot{constructor(e){super(e,"InvalidPDFException")}}class MissingPDFException extends ot{constructor(e){super(e,"MissingPDFException")}}class UnexpectedResponseException extends ot{constructor(e,t){super(e,"UnexpectedResponseException");this.status=t}}class FormatError extends ot{constructor(e){super(e,"FormatError")}}class AbortException extends ot{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,i=8192;if(t<i)return String.fromCharCode.apply(null,e);const a=[];for(let s=0;s<t;s+=i){const r=Math.min(s+i,t),n=e.subarray(s,r);a.push(String.fromCharCode.apply(null,n))}return a.join("")}function stringToBytes(e){"string"!=typeof e&&unreachable("Invalid argument for stringToBytes");const t=e.length,i=new Uint8Array(t);for(let a=0;a<t;++a)i[a]=255&e.charCodeAt(a);return i}function string32(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)}function objectSize(e){return Object.keys(e).length}class FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const e=new Uint8Array(4);e[0]=1;return 1===new Uint32Array(e.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get platform(){return"undefined"!=typeof navigator&&"string"==typeof navigator?.platform?shadow(this,"platform",{isMac:navigator.platform.includes("Mac"),isWindows:navigator.platform.includes("Win"),isFirefox:"string"==typeof navigator?.userAgent&&navigator.userAgent.includes("Firefox")}):shadow(this,"platform",{isMac:!1,isWindows:!1,isFirefox:!1})}static get isCSSRoundSupported(){return shadow(this,"isCSSRoundSupported",globalThis.CSS?.supports?.("width: round(1.5px, 1px)"))}}const It=Array.from(Array(256).keys(),(e=>e.toString(16).padStart(2,"0")));class Util{static makeHexColor(e,t,i){return`#${It[e]}${It[t]}${It[i]}`}static scaleMinMax(e,t){let i;if(e[0]){if(e[0]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[0];t[2]*=e[0];if(e[3]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[3];t[3]*=e[3]}else{i=t[0];t[0]=t[1];t[1]=i;i=t[2];t[2]=t[3];t[3]=i;if(e[1]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[1];t[3]*=e[1];if(e[2]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[2];t[2]*=e[2]}t[0]+=e[4];t[1]+=e[5];t[2]+=e[4];t[3]+=e[5]}static transform(e,t){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]*t[2]+e[3]*t[3],e[0]*t[4]+e[2]*t[5]+e[4],e[1]*t[4]+e[3]*t[5]+e[5]]}static applyTransform(e,t){return[e[0]*t[0]+e[1]*t[2]+t[4],e[0]*t[1]+e[1]*t[3]+t[5]]}static applyInverseTransform(e,t){const i=t[0]*t[3]-t[1]*t[2];return[(e[0]*t[3]-e[1]*t[2]+t[2]*t[5]-t[4]*t[3])/i,(-e[0]*t[1]+e[1]*t[0]+t[4]*t[1]-t[5]*t[0])/i]}static getAxialAlignedBoundingBox(e,t){const i=this.applyTransform(e,t),a=this.applyTransform(e.slice(2,4),t),s=this.applyTransform([e[0],e[3]],t),r=this.applyTransform([e[2],e[1]],t);return[Math.min(i[0],a[0],s[0],r[0]),Math.min(i[1],a[1],s[1],r[1]),Math.max(i[0],a[0],s[0],r[0]),Math.max(i[1],a[1],s[1],r[1])]}static inverseTransform(e){const t=e[0]*e[3]-e[1]*e[2];return[e[3]/t,-e[1]/t,-e[2]/t,e[0]/t,(e[2]*e[5]-e[4]*e[3])/t,(e[4]*e[1]-e[5]*e[0])/t]}static singularValueDecompose2dScale(e){const t=[e[0],e[2],e[1],e[3]],i=e[0]*t[0]+e[1]*t[2],a=e[0]*t[1]+e[1]*t[3],s=e[2]*t[0]+e[3]*t[2],r=e[2]*t[1]+e[3]*t[3],n=(i+r)/2,g=Math.sqrt((i+r)**2-4*(i*r-s*a))/2,o=n+g||1,c=n-g||1;return[Math.sqrt(o),Math.sqrt(c)]}static normalizeRect(e){const t=e.slice(0);if(e[0]>e[2]){t[0]=e[2];t[2]=e[0]}if(e[1]>e[3]){t[1]=e[3];t[3]=e[1]}return t}static intersect(e,t){const i=Math.max(Math.min(e[0],e[2]),Math.min(t[0],t[2])),a=Math.min(Math.max(e[0],e[2]),Math.max(t[0],t[2]));if(i>a)return null;const s=Math.max(Math.min(e[1],e[3]),Math.min(t[1],t[3])),r=Math.min(Math.max(e[1],e[3]),Math.max(t[1],t[3]));return s>r?null:[i,s,a,r]}static#A(e,t,i,a,s,r,n,g,o,c){if(o<=0||o>=1)return;const C=1-o,h=o*o,l=h*o,Q=C*(C*(C*e+3*o*t)+3*h*i)+l*a,E=C*(C*(C*s+3*o*r)+3*h*n)+l*g;c[0]=Math.min(c[0],Q);c[1]=Math.min(c[1],E);c[2]=Math.max(c[2],Q);c[3]=Math.max(c[3],E)}static#e(e,t,i,a,s,r,n,g,o,c,C,h){if(Math.abs(o)<1e-12){Math.abs(c)>=1e-12&&this.#A(e,t,i,a,s,r,n,g,-C/c,h);return}const l=c**2-4*C*o;if(l<0)return;const Q=Math.sqrt(l),E=2*o;this.#A(e,t,i,a,s,r,n,g,(-c+Q)/E,h);this.#A(e,t,i,a,s,r,n,g,(-c-Q)/E,h)}static bezierBoundingBox(e,t,i,a,s,r,n,g,o){if(o){o[0]=Math.min(o[0],e,n);o[1]=Math.min(o[1],t,g);o[2]=Math.max(o[2],e,n);o[3]=Math.max(o[3],t,g)}else o=[Math.min(e,n),Math.min(t,g),Math.max(e,n),Math.max(t,g)];this.#e(e,i,s,n,t,a,r,g,3*(3*(i-s)-e+n),6*(e-2*i+s),3*(i-e),o);this.#e(e,i,s,n,t,a,r,g,3*(3*(a-r)-t+g),6*(t-2*a+r),3*(a-t),o);return o}}const ct=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,728,711,710,729,733,731,730,732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8226,8224,8225,8230,8212,8211,402,8260,8249,8250,8722,8240,8222,8220,8221,8216,8217,8218,8482,64257,64258,321,338,352,376,381,305,322,339,353,382,0,8364];function stringToPDFString(e){if(e[0]>="ï"){let t;if("þ"===e[0]&&"ÿ"===e[1]){t="utf-16be";e.length%2==1&&(e=e.slice(0,-1))}else if("ÿ"===e[0]&&"þ"===e[1]){t="utf-16le";e.length%2==1&&(e=e.slice(0,-1))}else"ï"===e[0]&&"»"===e[1]&&"¿"===e[2]&&(t="utf-8");if(t)try{const i=new TextDecoder(t,{fatal:!0}),a=stringToBytes(e),s=i.decode(a);return s.includes("")?s.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g,""):s}catch(e){warn(`stringToPDFString: "${e}".`)}}const t=[];for(let i=0,a=e.length;i<a;i++){const s=e.charCodeAt(i);if(27===s){for(;++i<a&&27!==e.charCodeAt(i););continue}const r=ct[s];t.push(r?String.fromCharCode(r):e.charAt(i))}return t.join("")}function stringToUTF8String(e){return decodeURIComponent(escape(e))}function utf8StringToString(e){return unescape(encodeURIComponent(e))}function isArrayEqual(e,t){if(e.length!==t.length)return!1;for(let i=0,a=e.length;i<a;i++)if(e[i]!==t[i])return!1;return!0}function getModificationDate(e=new Date){return[e.getUTCFullYear().toString(),(e.getUTCMonth()+1).toString().padStart(2,"0"),e.getUTCDate().toString().padStart(2,"0"),e.getUTCHours().toString().padStart(2,"0"),e.getUTCMinutes().toString().padStart(2,"0"),e.getUTCSeconds().toString().padStart(2,"0")].join("")}let Ct=null,ht=null;const Bt=0,lt=1,Qt=2,Et=3,ut=4,dt=5,ft=6,pt=7,mt=8,yt=Symbol("CIRCULAR_REF"),wt=Symbol("EOF");let Dt=Object.create(null),bt=Object.create(null),Ft=Object.create(null);class Name{constructor(e){this.name=e}static get(e){return bt[e]||=new Name(e)}}class Cmd{constructor(e){this.cmd=e}static get(e){return Dt[e]||=new Cmd(e)}}const St=function nonSerializableClosure(){return St};class Dict{constructor(e=null){this._map=Object.create(null);this.xref=e;this.objId=null;this.suppressEncryption=!1;this.__nonSerializable__=St}assignXref(e){this.xref=e}get size(){return Object.keys(this._map).length}get(e,t,i){let a=this._map[e];if(void 0===a&&void 0!==t){a=this._map[t];void 0===a&&void 0!==i&&(a=this._map[i])}return a instanceof Ref&&this.xref?this.xref.fetch(a,this.suppressEncryption):a}async getAsync(e,t,i){let a=this._map[e];if(void 0===a&&void 0!==t){a=this._map[t];void 0===a&&void 0!==i&&(a=this._map[i])}return a instanceof Ref&&this.xref?this.xref.fetchAsync(a,this.suppressEncryption):a}getArray(e,t,i){let a=this._map[e];if(void 0===a&&void 0!==t){a=this._map[t];void 0===a&&void 0!==i&&(a=this._map[i])}a instanceof Ref&&this.xref&&(a=this.xref.fetch(a,this.suppressEncryption));if(Array.isArray(a)){a=a.slice();for(let e=0,t=a.length;e<t;e++)a[e]instanceof Ref&&this.xref&&(a[e]=this.xref.fetch(a[e],this.suppressEncryption))}return a}getRaw(e){return this._map[e]}getKeys(){return Object.keys(this._map)}getRawValues(){return Object.values(this._map)}set(e,t){this._map[e]=t}has(e){return void 0!==this._map[e]}forEach(e){for(const t in this._map)e(t,this.get(t))}static get empty(){const e=new Dict(null);e.set=(e,t)=>{unreachable("Should not call `set` on the empty dictionary.")};return shadow(this,"empty",e)}static merge({xref:e,dictArray:t,mergeSubDicts:i=!1}){const a=new Dict(e),s=new Map;for(const e of t)if(e instanceof Dict)for(const[t,a]of Object.entries(e._map)){let e=s.get(t);if(void 0===e){e=[];s.set(t,e)}else if(!(i&&a instanceof Dict))continue;e.push(a)}for(const[t,i]of s){if(1===i.length||!(i[0]instanceof Dict)){a._map[t]=i[0];continue}const s=new Dict(e);for(const e of i)for(const[t,i]of Object.entries(e._map))void 0===s._map[t]&&(s._map[t]=i);s.size>0&&(a._map[t]=s)}s.clear();return a.size>0?a:Dict.empty}clone(){const e=new Dict(this.xref);for(const t of this.getKeys())e.set(t,this.getRaw(t));return e}delete(e){delete this._map[e]}}class Ref{constructor(e,t){this.num=e;this.gen=t}toString(){return 0===this.gen?`${this.num}R`:`${this.num}R${this.gen}`}static fromString(e){const t=Ft[e];if(t)return t;const i=/^(\d+)R(\d*)$/.exec(e);return i&&"0"!==i[1]?Ft[e]=new Ref(parseInt(i[1]),i[2]?parseInt(i[2]):0):null}static get(e,t){const i=0===t?`${e}R`:`${e}R${t}`;return Ft[i]||=new Ref(e,t)}}class RefSet{constructor(e=null){this._set=new Set(e?._set)}has(e){return this._set.has(e.toString())}put(e){this._set.add(e.toString())}remove(e){this._set.delete(e.toString())}[Symbol.iterator](){return this._set.values()}clear(){this._set.clear()}}class RefSetCache{constructor(){this._map=new Map}get size(){return this._map.size}get(e){return this._map.get(e.toString())}has(e){return this._map.has(e.toString())}put(e,t){this._map.set(e.toString(),t)}putAlias(e,t){this._map.set(e.toString(),this.get(t))}[Symbol.iterator](){return this._map.values()}clear(){this._map.clear()}*items(){for(const[e,t]of this._map)yield[Ref.fromString(e),t]}}function isName(e,t){return e instanceof Name&&(void 0===t||e.name===t)}function isCmd(e,t){return e instanceof Cmd&&(void 0===t||e.cmd===t)}function isDict(e,t){return e instanceof Dict&&(void 0===t||isName(e.get("Type"),t))}function isRefsEqual(e,t){return e.num===t.num&&e.gen===t.gen}class BaseStream{get length(){unreachable("Abstract getter `length` accessed")}get isEmpty(){unreachable("Abstract getter `isEmpty` accessed")}get isDataLoaded(){return shadow(this,"isDataLoaded",!0)}getByte(){unreachable("Abstract method `getByte` called")}getBytes(e){unreachable("Abstract method `getBytes` called")}async getImageData(e,t){return this.getBytes(e,t)}async asyncGetBytes(){unreachable("Abstract method `asyncGetBytes` called")}get isAsync(){return!1}get canAsyncDecodeImageFromBuffer(){return!1}peekByte(){const e=this.getByte();-1!==e&&this.pos--;return e}peekBytes(e){const t=this.getBytes(e);this.pos-=t.length;return t}getUint16(){const e=this.getByte(),t=this.getByte();return-1===e||-1===t?-1:(e<<8)+t}getInt32(){return(this.getByte()<<24)+(this.getByte()<<16)+(this.getByte()<<8)+this.getByte()}getByteRange(e,t){unreachable("Abstract method `getByteRange` called")}getString(e){return bytesToString(this.getBytes(e))}skip(e){this.pos+=e||1}reset(){unreachable("Abstract method `reset` called")}moveStart(){unreachable("Abstract method `moveStart` called")}makeSubStream(e,t,i=null){unreachable("Abstract method `makeSubStream` called")}getBaseStreams(){return null}}const kt=/^[1-9]\.\d$/;function getLookupTableFactory(e){let t;return function(){if(e){t=Object.create(null);e(t);e=null}return t}}class MissingDataException extends ot{constructor(e,t){super(`Missing data [${e}, ${t})`,"MissingDataException");this.begin=e;this.end=t}}class ParserEOFException extends ot{constructor(e){super(e,"ParserEOFException")}}class XRefEntryException extends ot{constructor(e){super(e,"XRefEntryException")}}class XRefParseException extends ot{constructor(e){super(e,"XRefParseException")}}function arrayBuffersToBytes(e){const t=e.length;if(0===t)return new Uint8Array(0);if(1===t)return new Uint8Array(e[0]);let i=0;for(let a=0;a<t;a++)i+=e[a].byteLength;const a=new Uint8Array(i);let s=0;for(let i=0;i<t;i++){const t=new Uint8Array(e[i]);a.set(t,s);s+=t.byteLength}return a}function getInheritableProperty({dict:e,key:t,getArray:i=!1,stopWhenFound:a=!0}){let s;const r=new RefSet;for(;e instanceof Dict&&(!e.objId||!r.has(e.objId));){e.objId&&r.put(e.objId);const n=i?e.getArray(t):e.get(t);if(void 0!==n){if(a)return n;(s||=[]).push(n)}e=e.get("Parent")}return s}const Rt=["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM","","X","XX","XXX","XL","L","LX","LXX","LXXX","XC","","I","II","III","IV","V","VI","VII","VIII","IX"];function toRomanNumerals(e,t=!1){assert(Number.isInteger(e)&&e>0,"The number should be a positive integer.");const i=[];let a;for(;e>=1e3;){e-=1e3;i.push("M")}a=e/100|0;e%=100;i.push(Rt[a]);a=e/10|0;e%=10;i.push(Rt[10+a]);i.push(Rt[20+e]);const s=i.join("");return t?s.toLowerCase():s}function log2(e){return e<=0?0:Math.ceil(Math.log2(e))}function readInt8(e,t){return e[t]<<24>>24}function readUint16(e,t){return e[t]<<8|e[t+1]}function readUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function isWhiteSpace(e){return 32===e||9===e||13===e||10===e}function isNumberArray(e,t){return Array.isArray(e)?(null===t||e.length===t)&&e.every((e=>"number"==typeof e)):ArrayBuffer.isView(e)&&(0===e.length||"number"==typeof e[0])&&(null===t||e.length===t)}function lookupMatrix(e,t){return isNumberArray(e,6)?e:t}function lookupRect(e,t){return isNumberArray(e,4)?e:t}function lookupNormalRect(e,t){return isNumberArray(e,4)?Util.normalizeRect(e):t}function parseXFAPath(e){const t=/(.+)\[(\d+)\]$/;return e.split(".").map((e=>{const i=e.match(t);return i?{name:i[1],pos:parseInt(i[2],10)}:{name:e,pos:0}}))}function escapePDFName(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<33||s>126||35===s||40===s||41===s||60===s||62===s||91===s||93===s||123===s||125===s||47===s||37===s){i<a&&t.push(e.substring(i,a));t.push(`#${s.toString(16)}`);i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function escapeString(e){return e.replaceAll(/([()\\\n\r])/g,(e=>"\n"===e?"\\n":"\r"===e?"\\r":`\\${e}`))}function _collectJS(e,t,i,a){if(!e)return;let s=null;if(e instanceof Ref){if(a.has(e))return;s=e;a.put(s);e=t.fetch(e)}if(Array.isArray(e))for(const s of e)_collectJS(s,t,i,a);else if(e instanceof Dict){if(isName(e.get("S"),"JavaScript")){const t=e.get("JS");let a;t instanceof BaseStream?a=t.getString():"string"==typeof t&&(a=t);a&&=stringToPDFString(a).replaceAll("\0","");a&&i.push(a)}_collectJS(e.getRaw("Next"),t,i,a)}s&&a.remove(s)}function collectActions(e,t,i){const a=Object.create(null),s=getInheritableProperty({dict:t,key:"AA",stopWhenFound:!1});if(s)for(let t=s.length-1;t>=0;t--){const r=s[t];if(r instanceof Dict)for(const t of r.getKeys()){const s=i[t];if(!s)continue;const n=[];_collectJS(r.getRaw(t),e,n,new RefSet);n.length>0&&(a[s]=n)}}if(t.has("A")){const i=[];_collectJS(t.get("A"),e,i,new RefSet);i.length>0&&(a.Action=i)}return objectSize(a)>0?a:null}const Nt={60:"<",62:">",38:"&",34:""",39:"'"};function*codePointIter(e){for(let t=0,i=e.length;t<i;t++){const i=e.codePointAt(t);i>55295&&(i<57344||i>65533)&&t++;yield i}}function encodeToXmlString(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.codePointAt(a);if(32<=s&&s<=126){const r=Nt[s];if(r){i<a&&t.push(e.substring(i,a));t.push(r);i=a+1}}else{i<a&&t.push(e.substring(i,a));t.push(`&#x${s.toString(16).toUpperCase()};`);s>55295&&(s<57344||s>65533)&&a++;i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function validateFontName(e,t=!1){const i=/^("|').*("|')$/.exec(e);if(i&&i[1]===i[2]){if(new RegExp(`[^\\\\]${i[1]}`).test(e.slice(1,-1))){t&&warn(`FontFamily contains unescaped ${i[1]}: ${e}.`);return!1}}else for(const i of e.split(/[ \t]+/))if(/^(\d|(-(\d|-)))/.test(i)||!/^[\w-\\]+$/.test(i)){t&&warn(`FontFamily contains invalid <custom-ident>: ${e}.`);return!1}return!0}function validateCSSFont(e){const t=new Set(["100","200","300","400","500","600","700","800","900","1000","normal","bold","bolder","lighter"]),{fontFamily:i,fontWeight:a,italicAngle:s}=e;if(!validateFontName(i,!0))return!1;const r=a?a.toString():"";e.fontWeight=t.has(r)?r:"400";const n=parseFloat(s);e.italicAngle=isNaN(n)||n<-90||n>90?"14":s.toString();return!0}function recoverJsURL(e){const t=new RegExp("^\\s*("+["app.launchURL","window.open","xfa.host.gotoURL"].join("|").replaceAll(".","\\.")+")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))","i").exec(e);if(t?.[2]){const e=t[2];let i=!1;"true"===t[3]&&"app.launchURL"===t[1]&&(i=!0);return{url:e,newWindow:i}}return null}function numberToString(e){if(Number.isInteger(e))return e.toString();const t=Math.round(100*e);return t%100==0?(t/100).toString():t%10==0?e.toFixed(1):e.toFixed(2)}function getNewAnnotationsMap(e){if(!e)return null;const t=new Map;for(const[i,a]of e){if(!i.startsWith(u))continue;let e=t.get(a.pageIndex);if(!e){e=[];t.set(a.pageIndex,e)}e.push(a)}return t.size>0?t:null}function stringToAsciiOrUTF16BE(e){return function isAscii(e){return/^[\x00-\x7F]*$/.test(e)}(e)?e:stringToUTF16String(e,!0)}function stringToUTF16HexString(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e.charCodeAt(i);t.push((a>>8&255).toString(16).padStart(2,"0"),(255&a).toString(16).padStart(2,"0"))}return t.join("")}function stringToUTF16String(e,t=!1){const i=[];t&&i.push("þÿ");for(let t=0,a=e.length;t<a;t++){const a=e.charCodeAt(t);i.push(String.fromCharCode(a>>8&255),String.fromCharCode(255&a))}return i.join("")}function getRotationMatrix(e,t,i){switch(e){case 90:return[0,1,-1,0,t,0];case 180:return[-1,0,0,-1,t,i];case 270:return[0,-1,1,0,0,i];default:throw new Error("Invalid rotation")}}function getSizeInBytes(e){return Math.ceil(Math.ceil(Math.log2(1+e))/8)}class Stream extends BaseStream{constructor(e,t,i,a){super();this.bytes=e instanceof Uint8Array?e:new Uint8Array(e);this.start=t||0;this.pos=this.start;this.end=t+i||this.bytes.length;this.dict=a}get length(){return this.end-this.start}get isEmpty(){return 0===this.length}getByte(){return this.pos>=this.end?-1:this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e)return t.subarray(i,a);let s=i+e;s>a&&(s=a);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);return this.bytes.subarray(e,t)}reset(){this.pos=this.start}moveStart(){this.start=this.pos}makeSubStream(e,t,i=null){return new Stream(this.bytes.buffer,e,t,i)}}class StringStream extends Stream{constructor(e){super(stringToBytes(e))}}class NullStream extends Stream{constructor(){super(new Uint8Array(0))}}class ChunkedStream extends Stream{constructor(e,t,i){super(new Uint8Array(e),0,e,null);this.chunkSize=t;this._loadedChunks=new Set;this.numChunks=Math.ceil(e/t);this.manager=i;this.progressiveDataLength=0;this.lastSuccessfulEnsureByteChunk=-1}getMissingChunks(){const e=[];for(let t=0,i=this.numChunks;t<i;++t)this._loadedChunks.has(t)||e.push(t);return e}get numChunksLoaded(){return this._loadedChunks.size}get isDataLoaded(){return this.numChunksLoaded===this.numChunks}onReceiveData(e,t){const i=this.chunkSize;if(e%i!=0)throw new Error(`Bad begin offset: ${e}`);const a=e+t.byteLength;if(a%i!=0&&a!==this.bytes.length)throw new Error(`Bad end offset: ${a}`);this.bytes.set(new Uint8Array(t),e);const s=Math.floor(e/i),r=Math.floor((a-1)/i)+1;for(let e=s;e<r;++e)this._loadedChunks.add(e)}onReceiveProgressiveData(e){let t=this.progressiveDataLength;const i=Math.floor(t/this.chunkSize);this.bytes.set(new Uint8Array(e),t);t+=e.byteLength;this.progressiveDataLength=t;const a=t>=this.end?this.numChunks:Math.floor(t/this.chunkSize);for(let e=i;e<a;++e)this._loadedChunks.add(e)}ensureByte(e){if(e<this.progressiveDataLength)return;const t=Math.floor(e/this.chunkSize);if(!(t>this.numChunks)&&t!==this.lastSuccessfulEnsureByteChunk){if(!this._loadedChunks.has(t))throw new MissingDataException(e,e+1);this.lastSuccessfulEnsureByteChunk=t}}ensureRange(e,t){if(e>=t)return;if(t<=this.progressiveDataLength)return;const i=Math.floor(e/this.chunkSize);if(i>this.numChunks)return;const a=Math.min(Math.floor((t-1)/this.chunkSize)+1,this.numChunks);for(let s=i;s<a;++s)if(!this._loadedChunks.has(s))throw new MissingDataException(e,t)}nextEmptyChunk(e){const t=this.numChunks;for(let i=0;i<t;++i){const a=(e+i)%t;if(!this._loadedChunks.has(a))return a}return null}hasChunk(e){return this._loadedChunks.has(e)}getByte(){const e=this.pos;if(e>=this.end)return-1;e>=this.progressiveDataLength&&this.ensureByte(e);return this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e){a>this.progressiveDataLength&&this.ensureRange(i,a);return t.subarray(i,a)}let s=i+e;s>a&&(s=a);s>this.progressiveDataLength&&this.ensureRange(i,s);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);t>this.progressiveDataLength&&this.ensureRange(e,t);return this.bytes.subarray(e,t)}makeSubStream(e,t,i=null){t?e+t>this.progressiveDataLength&&this.ensureRange(e,e+t):e>=this.progressiveDataLength&&this.ensureByte(e);function ChunkedStreamSubstream(){}ChunkedStreamSubstream.prototype=Object.create(this);ChunkedStreamSubstream.prototype.getMissingChunks=function(){const e=this.chunkSize,t=Math.floor(this.start/e),i=Math.floor((this.end-1)/e)+1,a=[];for(let e=t;e<i;++e)this._loadedChunks.has(e)||a.push(e);return a};Object.defineProperty(ChunkedStreamSubstream.prototype,"isDataLoaded",{get(){return this.numChunksLoaded===this.numChunks||0===this.getMissingChunks().length},configurable:!0});const a=new ChunkedStreamSubstream;a.pos=a.start=e;a.end=e+t||this.end;a.dict=i;return a}getBaseStreams(){return[this]}}class ChunkedStreamManager{constructor(e,t){this.length=t.length;this.chunkSize=t.rangeChunkSize;this.stream=new ChunkedStream(this.length,this.chunkSize,this);this.pdfNetworkStream=e;this.disableAutoFetch=t.disableAutoFetch;this.msgHandler=t.msgHandler;this.currRequestId=0;this._chunksNeededByRequest=new Map;this._requestsByChunk=new Map;this._promisesByRequest=new Map;this.progressiveDataLength=0;this.aborted=!1;this._loadedStreamCapability=Promise.withResolvers()}sendRequest(e,t){const i=this.pdfNetworkStream.getRangeReader(e,t);i.isStreamingSupported||(i.onProgress=this.onProgress.bind(this));let a=[],s=0;return new Promise(((e,t)=>{const readChunk=({value:r,done:n})=>{try{if(n){const t=arrayBuffersToBytes(a);a=null;e(t);return}s+=r.byteLength;i.isStreamingSupported&&this.onProgress({loaded:s});a.push(r);i.read().then(readChunk,t)}catch(e){t(e)}};i.read().then(readChunk,t)})).then((t=>{this.aborted||this.onReceiveData({chunk:t,begin:e})}))}requestAllChunks(e=!1){if(!e){const e=this.stream.getMissingChunks();this._requestChunks(e)}return this._loadedStreamCapability.promise}_requestChunks(e){const t=this.currRequestId++,i=new Set;this._chunksNeededByRequest.set(t,i);for(const t of e)this.stream.hasChunk(t)||i.add(t);if(0===i.size)return Promise.resolve();const a=Promise.withResolvers();this._promisesByRequest.set(t,a);const s=[];for(const e of i){let i=this._requestsByChunk.get(e);if(!i){i=[];this._requestsByChunk.set(e,i);s.push(e)}i.push(t)}if(s.length>0){const e=this.groupChunks(s);for(const t of e){const e=t.beginChunk*this.chunkSize,i=Math.min(t.endChunk*this.chunkSize,this.length);this.sendRequest(e,i).catch(a.reject)}}return a.promise.catch((e=>{if(!this.aborted)throw e}))}getStream(){return this.stream}requestRange(e,t){t=Math.min(t,this.length);const i=this.getBeginChunk(e),a=this.getEndChunk(t),s=[];for(let e=i;e<a;++e)s.push(e);return this._requestChunks(s)}requestRanges(e=[]){const t=[];for(const i of e){const e=this.getBeginChunk(i.begin),a=this.getEndChunk(i.end);for(let i=e;i<a;++i)t.includes(i)||t.push(i)}t.sort((function(e,t){return e-t}));return this._requestChunks(t)}groupChunks(e){const t=[];let i=-1,a=-1;for(let s=0,r=e.length;s<r;++s){const r=e[s];i<0&&(i=r);if(a>=0&&a+1!==r){t.push({beginChunk:i,endChunk:a+1});i=r}s+1===e.length&&t.push({beginChunk:i,endChunk:r+1});a=r}return t}onProgress(e){this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize+e.loaded,total:this.length})}onReceiveData(e){const t=e.chunk,i=void 0===e.begin,a=i?this.progressiveDataLength:e.begin,s=a+t.byteLength,r=Math.floor(a/this.chunkSize),n=s<this.length?Math.floor(s/this.chunkSize):Math.ceil(s/this.chunkSize);if(i){this.stream.onReceiveProgressiveData(t);this.progressiveDataLength=s}else this.stream.onReceiveData(a,t);this.stream.isDataLoaded&&this._loadedStreamCapability.resolve(this.stream);const g=[];for(let e=r;e<n;++e){const t=this._requestsByChunk.get(e);if(t){this._requestsByChunk.delete(e);for(const i of t){const t=this._chunksNeededByRequest.get(i);t.has(e)&&t.delete(e);t.size>0||g.push(i)}}}if(!this.disableAutoFetch&&0===this._requestsByChunk.size){let e;if(1===this.stream.numChunksLoaded){const t=this.stream.numChunks-1;this.stream.hasChunk(t)||(e=t)}else e=this.stream.nextEmptyChunk(n);Number.isInteger(e)&&this._requestChunks([e])}for(const e of g){const t=this._promisesByRequest.get(e);this._promisesByRequest.delete(e);t.resolve()}this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize,total:this.length})}onError(e){this._loadedStreamCapability.reject(e)}getBeginChunk(e){return Math.floor(e/this.chunkSize)}getEndChunk(e){return Math.floor((e-1)/this.chunkSize)+1}abort(e){this.aborted=!0;this.pdfNetworkStream?.cancelAllRequests(e);for(const t of this._promisesByRequest.values())t.reject(e)}}class ColorSpace{constructor(e,t){this.name=e;this.numComps=t}getRgb(e,t){const i=new Uint8ClampedArray(3);this.getRgbItem(e,t,i,0);return i}getRgbItem(e,t,i,a){unreachable("Should not call ColorSpace.getRgbItem")}getRgbBuffer(e,t,i,a,s,r,n){unreachable("Should not call ColorSpace.getRgbBuffer")}getOutputLength(e,t){unreachable("Should not call ColorSpace.getOutputLength")}isPassthrough(e){return!1}isDefaultDecode(e,t){return ColorSpace.isDefaultDecode(e,this.numComps)}fillRgb(e,t,i,a,s,r,n,g,o){const c=t*i;let C=null;const h=1<<n,l=i!==s||t!==a;if(this.isPassthrough(n))C=g;else if(1===this.numComps&&c>h&&"DeviceGray"!==this.name&&"DeviceRGB"!==this.name){const t=n<=8?new Uint8Array(h):new Uint16Array(h);for(let e=0;e<h;e++)t[e]=e;const i=new Uint8ClampedArray(3*h);this.getRgbBuffer(t,0,h,i,0,n,0);if(l){C=new Uint8Array(3*c);let e=0;for(let t=0;t<c;++t){const a=3*g[t];C[e++]=i[a];C[e++]=i[a+1];C[e++]=i[a+2]}}else{let t=0;for(let a=0;a<c;++a){const s=3*g[a];e[t++]=i[s];e[t++]=i[s+1];e[t++]=i[s+2];t+=o}}}else if(l){C=new Uint8ClampedArray(3*c);this.getRgbBuffer(g,0,c,C,0,n,0)}else this.getRgbBuffer(g,0,a*r,e,0,n,o);if(C)if(l)!function resizeRgbImage(e,t,i,a,s,r,n){n=1!==n?0:n;const g=i/s,o=a/r;let c,C=0;const h=new Uint16Array(s),l=3*i;for(let e=0;e<s;e++)h[e]=3*Math.floor(e*g);for(let i=0;i<r;i++){const a=Math.floor(i*o)*l;for(let i=0;i<s;i++){c=a+h[i];t[C++]=e[c++];t[C++]=e[c++];t[C++]=e[c++];C+=n}}}(C,e,t,i,a,s,o);else{let t=0,i=0;for(let s=0,n=a*r;s<n;s++){e[t++]=C[i++];e[t++]=C[i++];e[t++]=C[i++];t+=o}}}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!0)}static _cache(e,t,i,a){if(!i)throw new Error('ColorSpace._cache - expected "localColorSpaceCache" argument.');if(!a)throw new Error('ColorSpace._cache - expected "parsedColorSpace" argument.');let s,r;if(e instanceof Ref){r=e;e=t.fetch(e)}e instanceof Name&&(s=e.name);(s||r)&&i.set(s,r,a)}static getCached(e,t,i){if(!i)throw new Error('ColorSpace.getCached - expected "localColorSpaceCache" argument.');if(e instanceof Ref){const a=i.getByRef(e);if(a)return a;try{e=t.fetch(e)}catch(e){if(e instanceof MissingDataException)throw e}}if(e instanceof Name){const t=i.getByName(e.name);if(t)return t}return null}static async parseAsync({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this._parse(e,t,i,a);this._cache(e,t,s,r);return r}static parse({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this.getCached(e,t,s);if(r)return r;const n=this._parse(e,t,i,a);this._cache(e,t,s,n);return n}static _parse(e,t,i=null,a){if((e=t.fetchIfRef(e))instanceof Name)switch(e.name){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"DeviceRGBA":return this.singletons.rgba;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"Pattern":return new PatternCS(null);default:if(i instanceof Dict){const s=i.get("ColorSpace");if(s instanceof Dict){const r=s.get(e.name);if(r){if(r instanceof Name)return this._parse(r,t,i,a);e=r;break}}}warn(`Unrecognized ColorSpace: ${e.name}`);return this.singletons.gray}if(Array.isArray(e)){const s=t.fetchIfRef(e[0]).name;let r,n,g,o,c,C;switch(s){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"CalGray":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.get("Gamma");return new CalGrayCS(o,c,C);case"CalRGB":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.getArray("Gamma");const h=r.getArray("Matrix");return new CalRGBCS(o,c,C,h);case"ICCBased":const l=t.fetchIfRef(e[1]).dict;n=l.get("N");const Q=l.get("Alternate");if(Q){const e=this._parse(Q,t,i,a);if(e.numComps===n)return e;warn("ICCBased color space: Ignoring incorrect /Alternate entry.")}if(1===n)return this.singletons.gray;if(3===n)return this.singletons.rgb;if(4===n)return this.singletons.cmyk;break;case"Pattern":g=e[1]||null;g&&(g=this._parse(g,t,i,a));return new PatternCS(g);case"I":case"Indexed":g=this._parse(e[1],t,i,a);const E=t.fetchIfRef(e[2])+1,u=t.fetchIfRef(e[3]);return new IndexedCS(g,E,u);case"Separation":case"DeviceN":const d=t.fetchIfRef(e[1]);n=Array.isArray(d)?d.length:1;g=this._parse(e[2],t,i,a);const f=a.create(e[3]);return new AlternateCS(n,g,f);case"Lab":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");const p=r.getArray("Range");return new LabCS(o,c,p);default:warn(`Unimplemented ColorSpace object: ${s}`);return this.singletons.gray}}warn(`Unrecognized ColorSpace object: ${e}`);return this.singletons.gray}static isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2*t!==e.length){warn("The decode map is not the correct length");return!0}for(let t=0,i=e.length;t<i;t+=2)if(0!==e[t]||1!==e[t+1])return!1;return!0}static get singletons(){return shadow(this,"singletons",{get gray(){return shadow(this,"gray",new DeviceGrayCS)},get rgb(){return shadow(this,"rgb",new DeviceRgbCS)},get rgba(){return shadow(this,"rgba",new DeviceRgbaCS)},get cmyk(){return shadow(this,"cmyk",new DeviceCmykCS)}})}}class AlternateCS extends ColorSpace{constructor(e,t,i){super("Alternate",e);this.base=t;this.tintFn=i;this.tmpBuf=new Float32Array(t.numComps)}getRgbItem(e,t,i,a){const s=this.tmpBuf;this.tintFn(e,t,s,0);this.base.getRgbItem(s,0,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.tintFn,o=this.base,c=1/((1<<r)-1),C=o.numComps,h=o.usesZeroToOneRange,l=(o.isPassthrough(8)||!h)&&0===n;let Q=l?s:0;const E=l?a:new Uint8ClampedArray(C*i),u=this.numComps,d=new Float32Array(u),f=new Float32Array(C);let p,m;for(p=0;p<i;p++){for(m=0;m<u;m++)d[m]=e[t++]*c;g(d,0,f,0);if(h)for(m=0;m<C;m++)E[Q++]=255*f[m];else{o.getRgbItem(f,0,E,Q);Q+=C}}l||o.getRgbBuffer(E,0,i,a,s,8,n)}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps/this.numComps,t)}}class PatternCS extends ColorSpace{constructor(e){super("Pattern",null);this.base=e}isDefaultDecode(e,t){unreachable("Should not call PatternCS.isDefaultDecode")}}class IndexedCS extends ColorSpace{constructor(e,t,i){super("Indexed",1);this.base=e;this.highVal=t;const a=e.numComps*t;this.lookup=new Uint8Array(a);if(i instanceof BaseStream){const e=i.getBytes(a);this.lookup.set(e)}else{if("string"!=typeof i)throw new FormatError(`IndexedCS - unrecognized lookup table: ${i}`);for(let e=0;e<a;++e)this.lookup[e]=255&i.charCodeAt(e)}}getRgbItem(e,t,i,a){const s=this.base.numComps,r=e[t]*s;this.base.getRgbBuffer(this.lookup,r,1,i,a,8,0)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.base,o=g.numComps,c=g.getOutputLength(o,n),C=this.lookup;for(let r=0;r<i;++r){const i=e[t++]*o;g.getRgbBuffer(C,i,1,a,s,8,n);s+=c}}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps,t)}isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2!==e.length){warn("Decode map length is not correct");return!0}if(!Number.isInteger(t)||t<1){warn("Bits per component is not correct");return!0}return 0===e[0]&&e[1]===(1<<t)-1}}class DeviceGrayCS extends ColorSpace{constructor(){super("DeviceGray",1)}getRgbItem(e,t,i,a){const s=255*e[t];i[a]=i[a+1]=i[a+2]=s}getRgbBuffer(e,t,i,a,s,r,n){const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){const t=g*e[o++];a[c++]=t;a[c++]=t;a[c++]=t;c+=n}}getOutputLength(e,t){return e*(3+t)}}class DeviceRgbCS extends ColorSpace{constructor(){super("DeviceRGB",3)}getRgbItem(e,t,i,a){i[a]=255*e[t];i[a+1]=255*e[t+1];i[a+2]=255*e[t+2]}getRgbBuffer(e,t,i,a,s,r,n){if(8===r&&0===n){a.set(e.subarray(t,t+3*i),s);return}const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){a[c++]=g*e[o++];a[c++]=g*e[o++];a[c++]=g*e[o++];c+=n}}getOutputLength(e,t){return e*(3+t)/3|0}isPassthrough(e){return 8===e}}class DeviceRgbaCS extends ColorSpace{constructor(){super("DeviceRGBA",4)}getOutputLength(e,t){return 4*e}isPassthrough(e){return 8===e}}class DeviceCmykCS extends ColorSpace{constructor(){super("DeviceCMYK",4)}#t(e,t,i,a,s){const r=e[t]*i,n=e[t+1]*i,g=e[t+2]*i,o=e[t+3]*i;a[s]=255+r*(-4.387332384609988*r+54.48615194189176*n+18.82290502165302*g+212.25662451639585*o-285.2331026137004)+n*(1.7149763477362134*n-5.6096736904047315*g+-17.873870861415444*o-5.497006427196366)+g*(-2.5217340131683033*g-21.248923337353073*o+17.5119270841813)+o*(-21.86122147463605*o-189.48180835922747);a[s+1]=255+r*(8.841041422036149*r+60.118027045597366*n+6.871425592049007*g+31.159100130055922*o-79.2970844816548)+n*(-15.310361306967817*n+17.575251261109482*g+131.35250912493976*o-190.9453302588951)+g*(4.444339102852739*g+9.8632861493405*o-24.86741582555878)+o*(-20.737325471181034*o-187.80453709719578);a[s+2]=255+r*(.8842522430003296*r+8.078677503112928*n+30.89978309703729*g-.23883238689178934*o-14.183576799673286)+n*(10.49593273432072*n+63.02378494754052*g+50.606957656360734*o-112.23884253719248)+g*(.03296041114873217*g+115.60384449646641*o-193.58209356861505)+o*(-22.33816807309886*o-180.12613974708367)}getRgbItem(e,t,i,a){this.#t(e,t,1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=4;s+=3+n}}getOutputLength(e,t){return e/4*(3+t)|0}}class CalGrayCS extends ColorSpace{constructor(e,t,i){super("CalGray",1);if(!e)throw new FormatError("WhitePoint missing - required for color space CalGray");[this.XW,this.YW,this.ZW]=e;[this.XB,this.YB,this.ZB]=t||[0,0,0];this.G=i||1;if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(this.XB<0||this.YB<0||this.ZB<0){info(`Invalid BlackPoint for ${this.name}, falling back to default.`);this.XB=this.YB=this.ZB=0}0===this.XB&&0===this.YB&&0===this.ZB||warn(`${this.name}, BlackPoint: XB: ${this.XB}, YB: ${this.YB}, ZB: ${this.ZB}, only default values are supported.`);if(this.G<1){info(`Invalid Gamma: ${this.G} for ${this.name}, falling back to default.`);this.G=1}}#t(e,t,i,a,s){const r=(e[t]*s)**this.G,n=this.YW*r,g=Math.max(295.8*n**.3333333333333333-40.8,0);i[a]=g;i[a+1]=g;i[a+2]=g}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=1;s+=3+n}}getOutputLength(e,t){return e*(3+t)}}class CalRGBCS extends ColorSpace{static#i=new Float32Array([.8951,.2664,-.1614,-.7502,1.7135,.0367,.0389,-.0685,1.0296]);static#a=new Float32Array([.9869929,-.1470543,.1599627,.4323053,.5183603,.0492912,-.0085287,.0400428,.9684867]);static#s=new Float32Array([3.2404542,-1.5371385,-.4985314,-.969266,1.8760108,.041556,.0556434,-.2040259,1.0572252]);static#r=new Float32Array([1,1,1]);static#n=new Float32Array(3);static#g=new Float32Array(3);static#o=new Float32Array(3);static#I=(24/116)**3/8;constructor(e,t,i,a){super("CalRGB",3);if(!e)throw new FormatError("WhitePoint missing - required for color space CalRGB");const[s,r,n]=this.whitePoint=e,[g,o,c]=this.blackPoint=t||new Float32Array(3);[this.GR,this.GG,this.GB]=i||new Float32Array([1,1,1]);[this.MXA,this.MYA,this.MZA,this.MXB,this.MYB,this.MZB,this.MXC,this.MYC,this.MZC]=a||new Float32Array([1,0,0,0,1,0,0,0,1]);if(s<0||n<0||1!==r)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(g<0||o<0||c<0){info(`Invalid BlackPoint for ${this.name} [${g}, ${o}, ${c}], falling back to default.`);this.blackPoint=new Float32Array(3)}if(this.GR<0||this.GG<0||this.GB<0){info(`Invalid Gamma [${this.GR}, ${this.GG}, ${this.GB}] for ${this.name}, falling back to default.`);this.GR=this.GG=this.GB=1}}#c(e,t,i){i[0]=e[0]*t[0]+e[1]*t[1]+e[2]*t[2];i[1]=e[3]*t[0]+e[4]*t[1]+e[5]*t[2];i[2]=e[6]*t[0]+e[7]*t[1]+e[8]*t[2]}#C(e,t,i){i[0]=1*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1*t[2]/e[2]}#h(e,t,i){i[0]=.95047*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1.08883*t[2]/e[2]}#B(e){return e<=.0031308?this.#l(0,1,12.92*e):e>=.99554525?1:this.#l(0,1,1.055*e**(1/2.4)-.055)}#l(e,t,i){return Math.max(e,Math.min(t,i))}#Q(e){return e<0?-this.#Q(-e):e>8?((e+16)/116)**3:e*CalRGBCS.#I}#E(e,t,i){if(0===e[0]&&0===e[1]&&0===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=this.#Q(0),s=(1-a)/(1-this.#Q(e[0])),r=1-s,n=(1-a)/(1-this.#Q(e[1])),g=1-n,o=(1-a)/(1-this.#Q(e[2])),c=1-o;i[0]=t[0]*s+r;i[1]=t[1]*n+g;i[2]=t[2]*o+c}#u(e,t,i){if(1===e[0]&&1===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#C(e,a,s);this.#c(CalRGBCS.#a,s,i)}#d(e,t,i){const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#h(e,a,s);this.#c(CalRGBCS.#a,s,i)}#t(e,t,i,a,s){const r=this.#l(0,1,e[t]*s),n=this.#l(0,1,e[t+1]*s),g=this.#l(0,1,e[t+2]*s),o=1===r?1:r**this.GR,c=1===n?1:n**this.GG,C=1===g?1:g**this.GB,h=this.MXA*o+this.MXB*c+this.MXC*C,l=this.MYA*o+this.MYB*c+this.MYC*C,Q=this.MZA*o+this.MZB*c+this.MZC*C,E=CalRGBCS.#g;E[0]=h;E[1]=l;E[2]=Q;const u=CalRGBCS.#o;this.#u(this.whitePoint,E,u);const d=CalRGBCS.#g;this.#E(this.blackPoint,u,d);const f=CalRGBCS.#o;this.#d(CalRGBCS.#r,d,f);const p=CalRGBCS.#g;this.#c(CalRGBCS.#s,f,p);i[a]=255*this.#B(p[0]);i[a+1]=255*this.#B(p[1]);i[a+2]=255*this.#B(p[2])}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}}class LabCS extends ColorSpace{constructor(e,t,i){super("Lab",3);if(!e)throw new FormatError("WhitePoint missing - required for color space Lab");[this.XW,this.YW,this.ZW]=e;[this.amin,this.amax,this.bmin,this.bmax]=i||[-100,100,-100,100];[this.XB,this.YB,this.ZB]=t||[0,0,0];if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError("Invalid WhitePoint components, no fallback available");if(this.XB<0||this.YB<0||this.ZB<0){info("Invalid BlackPoint, falling back to default");this.XB=this.YB=this.ZB=0}if(this.amin>this.amax||this.bmin>this.bmax){info("Invalid Range, falling back to defaults");this.amin=-100;this.amax=100;this.bmin=-100;this.bmax=100}}#f(e){return e>=6/29?e**3:108/841*(e-4/29)}#p(e,t,i,a){return i+e*(a-i)/t}#t(e,t,i,a,s){let r=e[t],n=e[t+1],g=e[t+2];if(!1!==i){r=this.#p(r,i,0,100);n=this.#p(n,i,this.amin,this.amax);g=this.#p(g,i,this.bmin,this.bmax)}n>this.amax?n=this.amax:n<this.amin&&(n=this.amin);g>this.bmax?g=this.bmax:g<this.bmin&&(g=this.bmin);const o=(r+16)/116,c=o+n/500,C=o-g/200,h=this.XW*this.#f(c),l=this.YW*this.#f(o),Q=this.ZW*this.#f(C);let E,u,d;if(this.ZW<1){E=3.1339*h+-1.617*l+-.4906*Q;u=-.9785*h+1.916*l+.0333*Q;d=.072*h+-.229*l+1.4057*Q}else{E=3.2406*h+-1.5372*l+-.4986*Q;u=-.9689*h+1.8758*l+.0415*Q;d=.0557*h+-.204*l+1.057*Q}a[s]=255*Math.sqrt(E);a[s+1]=255*Math.sqrt(u);a[s+2]=255*Math.sqrt(d)}getRgbItem(e,t,i,a){this.#t(e,t,!1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=(1<<r)-1;for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}isDefaultDecode(e,t){return!0}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!1)}}function hexToInt(e,t){let i=0;for(let a=0;a<=t;a++)i=i<<8|e[a];return i>>>0}function hexToStr(e,t){return 1===t?String.fromCharCode(e[0],e[1]):3===t?String.fromCharCode(e[0],e[1],e[2],e[3]):String.fromCharCode(...e.subarray(0,t+1))}function addHex(e,t,i){let a=0;for(let s=i;s>=0;s--){a+=e[s]+t[s];e[s]=255&a;a>>=8}}function incHex(e,t){let i=1;for(let a=t;a>=0&&i>0;a--){i+=e[a];e[a]=255&i;i>>=8}}const Gt=16;class BinaryCMapStream{constructor(e){this.buffer=e;this.pos=0;this.end=e.length;this.tmpBuf=new Uint8Array(19)}readByte(){return this.pos>=this.end?-1:this.buffer[this.pos++]}readNumber(){let e,t=0;do{const i=this.readByte();if(i<0)throw new FormatError("unexpected EOF in bcmap");e=!(128&i);t=t<<7|127&i}while(!e);return t}readSigned(){const e=this.readNumber();return 1&e?~(e>>>1):e>>>1}readHex(e,t){e.set(this.buffer.subarray(this.pos,this.pos+t+1));this.pos+=t+1}readHexNumber(e,t){let i;const a=this.tmpBuf;let s=0;do{const e=this.readByte();if(e<0)throw new FormatError("unexpected EOF in bcmap");i=!(128&e);a[s++]=127&e}while(!i);let r=t,n=0,g=0;for(;r>=0;){for(;g<8&&a.length>0;){n|=a[--s]<<g;g+=7}e[r]=255&n;r--;n>>=8;g-=8}}readHexSigned(e,t){this.readHexNumber(e,t);const i=1&e[t]?255:0;let a=0;for(let s=0;s<=t;s++){a=(1&a)<<8|e[s];e[s]=a>>1^i}}readString(){const e=this.readNumber(),t=new Array(e);for(let i=0;i<e;i++)t[i]=this.readNumber();return String.fromCharCode(...t)}}class BinaryCMapReader{async process(e,t,i){const a=new BinaryCMapStream(e),s=a.readByte();t.vertical=!!(1&s);let r=null;const n=new Uint8Array(Gt),g=new Uint8Array(Gt),o=new Uint8Array(Gt),c=new Uint8Array(Gt),C=new Uint8Array(Gt);let h,l;for(;(l=a.readByte())>=0;){const e=l>>5;if(7===e){switch(31&l){case 0:a.readString();break;case 1:r=a.readString()}continue}const i=!!(16&l),s=15&l;if(s+1>Gt)throw new Error("BinaryCMapReader.process: Invalid dataSize.");const Q=1,E=a.readNumber();switch(e){case 0:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s));for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s))}break;case 1:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber();for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber()}break;case 2:a.readHex(o,s);h=a.readNumber();t.mapOne(hexToInt(o,s),h);for(let e=1;e<E;e++){incHex(o,s);if(!i){a.readHexNumber(C,s);addHex(o,C,s)}h=a.readSigned()+(h+1);t.mapOne(hexToInt(o,s),h)}break;case 3:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h);for(let e=1;e<E;e++){incHex(g,s);if(i)n.set(g);else{a.readHexNumber(n,s);addHex(n,g,s)}a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h)}break;case 4:a.readHex(o,Q);a.readHex(c,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(o,Q);if(!i){a.readHexNumber(C,Q);addHex(o,C,Q)}incHex(c,s);a.readHexSigned(C,s);addHex(c,C,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s))}break;case 5:a.readHex(n,Q);a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(g,Q);if(i)n.set(g);else{a.readHexNumber(n,Q);addHex(n,g,Q)}a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s))}break;default:throw new Error(`BinaryCMapReader.process - unknown type: ${e}`)}}return r?i(r):t}}const xt=new Uint8Array(0);class DecodeStream extends BaseStream{constructor(e){super();this._rawMinBufferLength=e||0;this.pos=0;this.bufferLength=0;this.eof=!1;this.buffer=xt;this.minBufferLength=512;if(e)for(;this.minBufferLength<e;)this.minBufferLength*=2}get isEmpty(){for(;!this.eof&&0===this.bufferLength;)this.readBlock();return 0===this.bufferLength}ensureBuffer(e){const t=this.buffer;if(e<=t.byteLength)return t;let i=this.minBufferLength;for(;i<e;)i*=2;const a=new Uint8Array(i);a.set(t);return this.buffer=a}getByte(){const e=this.pos;for(;this.bufferLength<=e;){if(this.eof)return-1;this.readBlock()}return this.buffer[this.pos++]}getBytes(e,t=null){const i=this.pos;let a;if(e){this.ensureBuffer(i+e);a=i+e;for(;!this.eof&&this.bufferLength<a;)this.readBlock(t);const s=this.bufferLength;a>s&&(a=s)}else{for(;!this.eof;)this.readBlock(t);a=this.bufferLength}this.pos=a;return this.buffer.subarray(i,a)}async getImageData(e,t=null){if(!this.canAsyncDecodeImageFromBuffer)return this.getBytes(e,t);const i=await this.stream.asyncGetBytes();return this.decodeImage(i,t)}reset(){this.pos=0}makeSubStream(e,t,i=null){if(void 0===t)for(;!this.eof;)this.readBlock();else{const i=e+t;for(;this.bufferLength<=i&&!this.eof;)this.readBlock()}return new Stream(this.buffer,e,t,i)}getBaseStreams(){return this.str?this.str.getBaseStreams():null}}class StreamsSequenceStream extends DecodeStream{constructor(e,t=null){let i=0;for(const t of e)i+=t instanceof DecodeStream?t._rawMinBufferLength:t.length;super(i);this.streams=e;this._onError=t}readBlock(){const e=this.streams;if(0===e.length){this.eof=!0;return}const t=e.shift();let i;try{i=t.getBytes()}catch(e){if(this._onError){this._onError(e,t.dict?.objId);return}throw e}const a=this.bufferLength,s=a+i.length;this.ensureBuffer(s).set(i,a);this.bufferLength=s}getBaseStreams(){const e=[];for(const t of this.streams){const i=t.getBaseStreams();i&&e.push(...i)}return e.length>0?e:null}}class Ascii85Stream extends DecodeStream{constructor(e,t){t&&(t*=.8);super(t);this.str=e;this.dict=e.dict;this.input=new Uint8Array(5)}readBlock(){const e=this.str;let t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();if(-1===t||126===t){this.eof=!0;return}const i=this.bufferLength;let a,s;if(122===t){a=this.ensureBuffer(i+4);for(s=0;s<4;++s)a[i+s]=0;this.bufferLength+=4}else{const r=this.input;r[0]=t;for(s=1;s<5;++s){t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();r[s]=t;if(-1===t||126===t)break}a=this.ensureBuffer(i+s-1);this.bufferLength+=s-1;if(s<5){for(;s<5;++s)r[s]=117;this.eof=!0}let n=0;for(s=0;s<5;++s)n=85*n+(r[s]-33);for(s=3;s>=0;--s){a[i+s]=255&n;n>>=8}}}}class AsciiHexStream extends DecodeStream{constructor(e,t){t&&(t*=.5);super(t);this.str=e;this.dict=e.dict;this.firstDigit=-1}readBlock(){const e=this.str.getBytes(8e3);if(!e.length){this.eof=!0;return}const t=e.length+1>>1,i=this.ensureBuffer(this.bufferLength+t);let a=this.bufferLength,s=this.firstDigit;for(const t of e){let e;if(t>=48&&t<=57)e=15&t;else{if(!(t>=65&&t<=70||t>=97&&t<=102)){if(62===t){this.eof=!0;break}continue}e=9+(15&t)}if(s<0)s=e;else{i[a++]=s<<4|e;s=-1}}if(s>=0&&this.eof){i[a++]=s<<4;s=-1}this.firstDigit=s;this.bufferLength=a}}const Ut=-1,Mt=[[-1,-1],[-1,-1],[7,8],[7,7],[6,6],[6,6],[6,5],[6,5],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2]],Lt=[[-1,-1],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[12,1984],[12,2048],[12,2112],[12,2176],[12,2240],[12,2304],[11,1856],[11,1856],[11,1920],[11,1920],[12,2368],[12,2432],[12,2496],[12,2560]],Ht=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[8,29],[8,29],[8,30],[8,30],[8,45],[8,45],[8,46],[8,46],[7,22],[7,22],[7,22],[7,22],[7,23],[7,23],[7,23],[7,23],[8,47],[8,47],[8,48],[8,48],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[7,20],[7,20],[7,20],[7,20],[8,33],[8,33],[8,34],[8,34],[8,35],[8,35],[8,36],[8,36],[8,37],[8,37],[8,38],[8,38],[7,19],[7,19],[7,19],[7,19],[8,31],[8,31],[8,32],[8,32],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[8,53],[8,53],[8,54],[8,54],[7,26],[7,26],[7,26],[7,26],[8,39],[8,39],[8,40],[8,40],[8,41],[8,41],[8,42],[8,42],[8,43],[8,43],[8,44],[8,44],[7,21],[7,21],[7,21],[7,21],[7,28],[7,28],[7,28],[7,28],[8,61],[8,61],[8,62],[8,62],[8,63],[8,63],[8,0],[8,0],[8,320],[8,320],[8,384],[8,384],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[7,27],[7,27],[7,27],[7,27],[8,59],[8,59],[8,60],[8,60],[9,1472],[9,1536],[9,1600],[9,1728],[7,18],[7,18],[7,18],[7,18],[7,24],[7,24],[7,24],[7,24],[8,49],[8,49],[8,50],[8,50],[8,51],[8,51],[8,52],[8,52],[7,25],[7,25],[7,25],[7,25],[8,55],[8,55],[8,56],[8,56],[8,57],[8,57],[8,58],[8,58],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[8,448],[8,448],[8,512],[8,512],[9,704],[9,768],[8,640],[8,640],[8,576],[8,576],[9,832],[9,896],[9,960],[9,1024],[9,1088],[9,1152],[9,1216],[9,1280],[9,1344],[9,1408],[7,256],[7,256],[7,256],[7,256],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7]],Jt=[[-1,-1],[-1,-1],[12,-2],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[11,1792],[11,1792],[12,1984],[12,1984],[12,2048],[12,2048],[12,2112],[12,2112],[12,2176],[12,2176],[12,2240],[12,2240],[12,2304],[12,2304],[11,1856],[11,1856],[11,1856],[11,1856],[11,1920],[11,1920],[11,1920],[11,1920],[12,2368],[12,2368],[12,2432],[12,2432],[12,2496],[12,2496],[12,2560],[12,2560],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[12,52],[12,52],[13,640],[13,704],[13,768],[13,832],[12,55],[12,55],[12,56],[12,56],[13,1280],[13,1344],[13,1408],[13,1472],[12,59],[12,59],[12,60],[12,60],[13,1536],[13,1600],[11,24],[11,24],[11,24],[11,24],[11,25],[11,25],[11,25],[11,25],[13,1664],[13,1728],[12,320],[12,320],[12,384],[12,384],[12,448],[12,448],[13,512],[13,576],[12,53],[12,53],[12,54],[12,54],[13,896],[13,960],[13,1024],[13,1088],[13,1152],[13,1216],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64]],Yt=[[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[11,23],[11,23],[12,50],[12,51],[12,44],[12,45],[12,46],[12,47],[12,57],[12,58],[12,61],[12,256],[10,16],[10,16],[10,16],[10,16],[10,17],[10,17],[10,17],[10,17],[12,48],[12,49],[12,62],[12,63],[12,30],[12,31],[12,32],[12,33],[12,40],[12,41],[11,22],[11,22],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[12,128],[12,192],[12,26],[12,27],[12,28],[12,29],[11,19],[11,19],[11,20],[11,20],[12,34],[12,35],[12,36],[12,37],[12,38],[12,39],[11,21],[11,21],[12,42],[12,43],[10,0],[10,0],[10,0],[10,0],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12]],vt=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[6,9],[6,8],[5,7],[5,7],[4,6],[4,6],[4,6],[4,6],[4,5],[4,5],[4,5],[4,5],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2]];class CCITTFaxDecoder{constructor(e,t={}){if(!e||"function"!=typeof e.next)throw new Error('CCITTFaxDecoder - invalid "source" parameter.');this.source=e;this.eof=!1;this.encoding=t.K||0;this.eoline=t.EndOfLine||!1;this.byteAlign=t.EncodedByteAlign||!1;this.columns=t.Columns||1728;this.rows=t.Rows||0;this.eoblock=t.EndOfBlock??!0;this.black=t.BlackIs1||!1;this.codingLine=new Uint32Array(this.columns+1);this.refLine=new Uint32Array(this.columns+2);this.codingLine[0]=this.columns;this.codingPos=0;this.row=0;this.nextLine2D=this.encoding<0;this.inputBits=0;this.inputBuf=0;this.outputBits=0;this.rowsDone=!1;let i;for(;0===(i=this._lookBits(12));)this._eatBits(1);1===i&&this._eatBits(12);if(this.encoding>0){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}}readNextChar(){if(this.eof)return-1;const e=this.refLine,t=this.codingLine,i=this.columns;let a,s,r,n,g;if(0===this.outputBits){this.rowsDone&&(this.eof=!0);if(this.eof)return-1;this.err=!1;let r,g,o;if(this.nextLine2D){for(n=0;t[n]<i;++n)e[n]=t[n];e[n++]=i;e[n]=i;t[0]=0;this.codingPos=0;a=0;s=0;for(;t[this.codingPos]<i;){r=this._getTwoDimCode();switch(r){case 0:this._addPixels(e[a+1],s);e[a+1]<i&&(a+=2);break;case 1:r=g=0;if(s){do{r+=o=this._getBlackCode()}while(o>=64);do{g+=o=this._getWhiteCode()}while(o>=64)}else{do{r+=o=this._getWhiteCode()}while(o>=64);do{g+=o=this._getBlackCode()}while(o>=64)}this._addPixels(t[this.codingPos]+r,s);t[this.codingPos]<i&&this._addPixels(t[this.codingPos]+g,1^s);for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2;break;case 7:this._addPixels(e[a]+3,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 5:this._addPixels(e[a]+2,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 3:this._addPixels(e[a]+1,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 2:this._addPixels(e[a],s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 8:this._addPixelsNeg(e[a]-3,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 6:this._addPixelsNeg(e[a]-2,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 4:this._addPixelsNeg(e[a]-1,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case Ut:this._addPixels(i,0);this.eof=!0;break;default:info("bad 2d code");this._addPixels(i,0);this.err=!0}}}else{t[0]=0;this.codingPos=0;s=0;for(;t[this.codingPos]<i;){r=0;if(s)do{r+=o=this._getBlackCode()}while(o>=64);else do{r+=o=this._getWhiteCode()}while(o>=64);this._addPixels(t[this.codingPos]+r,s);s^=1}}let c=!1;this.byteAlign&&(this.inputBits&=-8);if(this.eoblock||this.row!==this.rows-1){r=this._lookBits(12);if(this.eoline)for(;r!==Ut&&1!==r;){this._eatBits(1);r=this._lookBits(12)}else for(;0===r;){this._eatBits(1);r=this._lookBits(12)}if(1===r){this._eatBits(12);c=!0}else r===Ut&&(this.eof=!0)}else this.rowsDone=!0;if(!this.eof&&this.encoding>0&&!this.rowsDone){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}if(this.eoblock&&c&&this.byteAlign){r=this._lookBits(12);if(1===r){this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}if(this.encoding>=0)for(n=0;n<4;++n){r=this._lookBits(12);1!==r&&info("bad rtc code: "+r);this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}}this.eof=!0}}else if(this.err&&this.eoline){for(;;){r=this._lookBits(13);if(r===Ut){this.eof=!0;return-1}if(r>>1==1)break;this._eatBits(1)}this._eatBits(12);if(this.encoding>0){this._eatBits(1);this.nextLine2D=!(1&r)}}this.outputBits=t[0]>0?t[this.codingPos=0]:t[this.codingPos=1];this.row++}if(this.outputBits>=8){g=1&this.codingPos?0:255;this.outputBits-=8;if(0===this.outputBits&&t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}}else{r=8;g=0;do{if("number"!=typeof this.outputBits)throw new FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.');if(this.outputBits>r){g<<=r;1&this.codingPos||(g|=255>>8-r);this.outputBits-=r;r=0}else{g<<=this.outputBits;1&this.codingPos||(g|=255>>8-this.outputBits);r-=this.outputBits;this.outputBits=0;if(t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}else if(r>0){g<<=r;r=0}}}while(r)}this.black&&(g^=255);return g}_addPixels(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}this.codingPos=a}_addPixelsNeg(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}else if(e<i[a]){if(e<0){info("invalid code");this.err=!0;e=0}for(;a>0&&e<i[a-1];)--a;i[a]=e}this.codingPos=a}_findTableCode(e,t,i,a){const s=a||0;for(let a=e;a<=t;++a){let e=this._lookBits(a);if(e===Ut)return[!0,1,!1];a<t&&(e<<=t-a);if(!s||e>=s){const t=i[e-s];if(t[0]===a){this._eatBits(a);return[!0,t[1],!0]}}}return[!1,0,!1]}_getTwoDimCode(){let e,t=0;if(this.eoblock){t=this._lookBits(7);e=Mt[t];if(e?.[0]>0){this._eatBits(e[0]);return e[1]}}else{const e=this._findTableCode(1,7,Mt);if(e[0]&&e[2])return e[1]}info("Bad two dim code");return Ut}_getWhiteCode(){let e,t=0;if(this.eoblock){t=this._lookBits(12);if(t===Ut)return 1;e=t>>5==0?Lt[t]:Ht[t>>3];if(e[0]>0){this._eatBits(e[0]);return e[1]}}else{let e=this._findTableCode(1,9,Ht);if(e[0])return e[1];e=this._findTableCode(11,12,Lt);if(e[0])return e[1]}info("bad white code");this._eatBits(1);return 1}_getBlackCode(){let e,t;if(this.eoblock){e=this._lookBits(13);if(e===Ut)return 1;t=e>>7==0?Jt[e]:e>>9==0&&e>>7!=0?Yt[(e>>1)-64]:vt[e>>7];if(t[0]>0){this._eatBits(t[0]);return t[1]}}else{let e=this._findTableCode(2,6,vt);if(e[0])return e[1];e=this._findTableCode(7,12,Yt,64);if(e[0])return e[1];e=this._findTableCode(10,13,Jt);if(e[0])return e[1]}info("bad black code");this._eatBits(1);return 1}_lookBits(e){let t;for(;this.inputBits<e;){if(-1===(t=this.source.next()))return 0===this.inputBits?Ut:this.inputBuf<<e-this.inputBits&65535>>16-e;this.inputBuf=this.inputBuf<<8|t;this.inputBits+=8}return this.inputBuf>>this.inputBits-e&65535>>16-e}_eatBits(e){(this.inputBits-=e)<0&&(this.inputBits=0)}}class CCITTFaxStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;i instanceof Dict||(i=Dict.empty);const a={next:()=>e.getByte()};this.ccittFaxDecoder=new CCITTFaxDecoder(a,{K:i.get("K"),EndOfLine:i.get("EndOfLine"),EncodedByteAlign:i.get("EncodedByteAlign"),Columns:i.get("Columns"),Rows:i.get("Rows"),EndOfBlock:i.get("EndOfBlock"),BlackIs1:i.get("BlackIs1")})}readBlock(){for(;!this.eof;){const e=this.ccittFaxDecoder.readNextChar();if(-1===e){this.eof=!0;return}this.ensureBuffer(this.bufferLength+1);this.buffer[this.bufferLength++]=e}}}const Kt=new Int32Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Tt=new Int32Array([3,4,5,6,7,8,9,10,65547,65549,65551,65553,131091,131095,131099,131103,196643,196651,196659,196667,262211,262227,262243,262259,327811,327843,327875,327907,258,258,258]),qt=new Int32Array([1,2,3,4,65541,65543,131081,131085,196625,196633,262177,262193,327745,327777,393345,393409,459009,459137,524801,525057,590849,591361,657409,658433,724993,727041,794625,798721,868353,876545]),Ot=[new Int32Array([459008,524368,524304,524568,459024,524400,524336,590016,459016,524384,524320,589984,524288,524416,524352,590048,459012,524376,524312,589968,459028,524408,524344,590032,459020,524392,524328,59e4,524296,524424,524360,590064,459010,524372,524308,524572,459026,524404,524340,590024,459018,524388,524324,589992,524292,524420,524356,590056,459014,524380,524316,589976,459030,524412,524348,590040,459022,524396,524332,590008,524300,524428,524364,590072,459009,524370,524306,524570,459025,524402,524338,590020,459017,524386,524322,589988,524290,524418,524354,590052,459013,524378,524314,589972,459029,524410,524346,590036,459021,524394,524330,590004,524298,524426,524362,590068,459011,524374,524310,524574,459027,524406,524342,590028,459019,524390,524326,589996,524294,524422,524358,590060,459015,524382,524318,589980,459031,524414,524350,590044,459023,524398,524334,590012,524302,524430,524366,590076,459008,524369,524305,524569,459024,524401,524337,590018,459016,524385,524321,589986,524289,524417,524353,590050,459012,524377,524313,589970,459028,524409,524345,590034,459020,524393,524329,590002,524297,524425,524361,590066,459010,524373,524309,524573,459026,524405,524341,590026,459018,524389,524325,589994,524293,524421,524357,590058,459014,524381,524317,589978,459030,524413,524349,590042,459022,524397,524333,590010,524301,524429,524365,590074,459009,524371,524307,524571,459025,524403,524339,590022,459017,524387,524323,589990,524291,524419,524355,590054,459013,524379,524315,589974,459029,524411,524347,590038,459021,524395,524331,590006,524299,524427,524363,590070,459011,524375,524311,524575,459027,524407,524343,590030,459019,524391,524327,589998,524295,524423,524359,590062,459015,524383,524319,589982,459031,524415,524351,590046,459023,524399,524335,590014,524303,524431,524367,590078,459008,524368,524304,524568,459024,524400,524336,590017,459016,524384,524320,589985,524288,524416,524352,590049,459012,524376,524312,589969,459028,524408,524344,590033,459020,524392,524328,590001,524296,524424,524360,590065,459010,524372,524308,524572,459026,524404,524340,590025,459018,524388,524324,589993,524292,524420,524356,590057,459014,524380,524316,589977,459030,524412,524348,590041,459022,524396,524332,590009,524300,524428,524364,590073,459009,524370,524306,524570,459025,524402,524338,590021,459017,524386,524322,589989,524290,524418,524354,590053,459013,524378,524314,589973,459029,524410,524346,590037,459021,524394,524330,590005,524298,524426,524362,590069,459011,524374,524310,524574,459027,524406,524342,590029,459019,524390,524326,589997,524294,524422,524358,590061,459015,524382,524318,589981,459031,524414,524350,590045,459023,524398,524334,590013,524302,524430,524366,590077,459008,524369,524305,524569,459024,524401,524337,590019,459016,524385,524321,589987,524289,524417,524353,590051,459012,524377,524313,589971,459028,524409,524345,590035,459020,524393,524329,590003,524297,524425,524361,590067,459010,524373,524309,524573,459026,524405,524341,590027,459018,524389,524325,589995,524293,524421,524357,590059,459014,524381,524317,589979,459030,524413,524349,590043,459022,524397,524333,590011,524301,524429,524365,590075,459009,524371,524307,524571,459025,524403,524339,590023,459017,524387,524323,589991,524291,524419,524355,590055,459013,524379,524315,589975,459029,524411,524347,590039,459021,524395,524331,590007,524299,524427,524363,590071,459011,524375,524311,524575,459027,524407,524343,590031,459019,524391,524327,589999,524295,524423,524359,590063,459015,524383,524319,589983,459031,524415,524351,590047,459023,524399,524335,590015,524303,524431,524367,590079]),9],Pt=[new Int32Array([327680,327696,327688,327704,327684,327700,327692,327708,327682,327698,327690,327706,327686,327702,327694,0,327681,327697,327689,327705,327685,327701,327693,327709,327683,327699,327691,327707,327687,327703,327695,0]),5];class FlateStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict;const i=e.getByte(),a=e.getByte();if(-1===i||-1===a)throw new FormatError(`Invalid header in flate stream: ${i}, ${a}`);if(8!=(15&i))throw new FormatError(`Unknown compression method in flate stream: ${i}, ${a}`);if(((i<<8)+a)%31!=0)throw new FormatError(`Bad FCHECK in flate stream: ${i}, ${a}`);if(32&a)throw new FormatError(`FDICT bit set in flate stream: ${i}, ${a}`);this.codeSize=0;this.codeBuf=0}async getImageData(e,t){const i=await this.asyncGetBytes();return i?.subarray(0,e)||this.getBytes(e)}async asyncGetBytes(){this.str.reset();const e=this.str.getBytes();try{const{readable:t,writable:i}=new DecompressionStream("deflate"),a=i.getWriter();a.write(e);a.close();const s=[];let r=0;for await(const e of t){s.push(e);r+=e.byteLength}const n=new Uint8Array(r);let g=0;for(const e of s){n.set(e,g);g+=e.byteLength}return n}catch{this.str=new Stream(e,2,e.length,this.str.dict);this.reset();return null}}get isAsync(){return!0}getBits(e){const t=this.str;let i,a=this.codeSize,s=this.codeBuf;for(;a<e;){if(-1===(i=t.getByte()))throw new FormatError("Bad encoding in flate stream");s|=i<<a;a+=8}i=s&(1<<e)-1;this.codeBuf=s>>e;this.codeSize=a-=e;return i}getCode(e){const t=this.str,i=e[0],a=e[1];let s,r=this.codeSize,n=this.codeBuf;for(;r<a&&-1!==(s=t.getByte());){n|=s<<r;r+=8}const g=i[n&(1<<a)-1],o=g>>16,c=65535&g;if(o<1||r<o)throw new FormatError("Bad encoding in flate stream");this.codeBuf=n>>o;this.codeSize=r-o;return c}generateHuffmanTable(e){const t=e.length;let i,a=0;for(i=0;i<t;++i)e[i]>a&&(a=e[i]);const s=1<<a,r=new Int32Array(s);for(let n=1,g=0,o=2;n<=a;++n,g<<=1,o<<=1)for(let a=0;a<t;++a)if(e[a]===n){let e=0,t=g;for(i=0;i<n;++i){e=e<<1|1&t;t>>=1}for(i=e;i<s;i+=o)r[i]=n<<16|a;++g}return[r,a]}#m(e){info(e);this.eof=!0}readBlock(){let e,t,i;const a=this.str;try{t=this.getBits(3)}catch(e){this.#m(e.message);return}1&t&&(this.eof=!0);t>>=1;if(0===t){let t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let i=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}i|=t<<8;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let s=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}s|=t<<8;if(s!==(65535&~i)&&(0!==i||0!==s))throw new FormatError("Bad uncompressed block length in flate stream");this.codeBuf=0;this.codeSize=0;const r=this.bufferLength,n=r+i;e=this.ensureBuffer(n);this.bufferLength=n;if(0===i)-1===a.peekByte()&&(this.eof=!0);else{const t=a.getBytes(i);e.set(t,r);t.length<i&&(this.eof=!0)}return}let s,r;if(1===t){s=Ot;r=Pt}else{if(2!==t)throw new FormatError("Unknown block type in flate stream");{const e=this.getBits(5)+257,t=this.getBits(5)+1,a=this.getBits(4)+4,n=new Uint8Array(Kt.length);let g;for(g=0;g<a;++g)n[Kt[g]]=this.getBits(3);const o=this.generateHuffmanTable(n);i=0;g=0;const c=e+t,C=new Uint8Array(c);let h,l,Q;for(;g<c;){const e=this.getCode(o);if(16===e){h=2;l=3;Q=i}else if(17===e){h=3;l=3;Q=i=0}else{if(18!==e){C[g++]=i=e;continue}h=7;l=11;Q=i=0}let t=this.getBits(h)+l;for(;t-- >0;)C[g++]=Q}s=this.generateHuffmanTable(C.subarray(0,e));r=this.generateHuffmanTable(C.subarray(e,c))}}e=this.buffer;let n=e?e.length:0,g=this.bufferLength;for(;;){let t=this.getCode(s);if(t<256){if(g+1>=n){e=this.ensureBuffer(g+1);n=e.length}e[g++]=t;continue}if(256===t){this.bufferLength=g;return}t-=257;t=Tt[t];let a=t>>16;a>0&&(a=this.getBits(a));i=(65535&t)+a;t=this.getCode(r);t=qt[t];a=t>>16;a>0&&(a=this.getBits(a));const o=(65535&t)+a;if(g+i>=n){e=this.ensureBuffer(g+i);n=e.length}for(let t=0;t<i;++t,++g)e[g]=e[g-o]}}}const Wt=[{qe:22017,nmps:1,nlps:1,switchFlag:1},{qe:13313,nmps:2,nlps:6,switchFlag:0},{qe:6145,nmps:3,nlps:9,switchFlag:0},{qe:2753,nmps:4,nlps:12,switchFlag:0},{qe:1313,nmps:5,nlps:29,switchFlag:0},{qe:545,nmps:38,nlps:33,switchFlag:0},{qe:22017,nmps:7,nlps:6,switchFlag:1},{qe:21505,nmps:8,nlps:14,switchFlag:0},{qe:18433,nmps:9,nlps:14,switchFlag:0},{qe:14337,nmps:10,nlps:14,switchFlag:0},{qe:12289,nmps:11,nlps:17,switchFlag:0},{qe:9217,nmps:12,nlps:18,switchFlag:0},{qe:7169,nmps:13,nlps:20,switchFlag:0},{qe:5633,nmps:29,nlps:21,switchFlag:0},{qe:22017,nmps:15,nlps:14,switchFlag:1},{qe:21505,nmps:16,nlps:14,switchFlag:0},{qe:20737,nmps:17,nlps:15,switchFlag:0},{qe:18433,nmps:18,nlps:16,switchFlag:0},{qe:14337,nmps:19,nlps:17,switchFlag:0},{qe:13313,nmps:20,nlps:18,switchFlag:0},{qe:12289,nmps:21,nlps:19,switchFlag:0},{qe:10241,nmps:22,nlps:19,switchFlag:0},{qe:9217,nmps:23,nlps:20,switchFlag:0},{qe:8705,nmps:24,nlps:21,switchFlag:0},{qe:7169,nmps:25,nlps:22,switchFlag:0},{qe:6145,nmps:26,nlps:23,switchFlag:0},{qe:5633,nmps:27,nlps:24,switchFlag:0},{qe:5121,nmps:28,nlps:25,switchFlag:0},{qe:4609,nmps:29,nlps:26,switchFlag:0},{qe:4353,nmps:30,nlps:27,switchFlag:0},{qe:2753,nmps:31,nlps:28,switchFlag:0},{qe:2497,nmps:32,nlps:29,switchFlag:0},{qe:2209,nmps:33,nlps:30,switchFlag:0},{qe:1313,nmps:34,nlps:31,switchFlag:0},{qe:1089,nmps:35,nlps:32,switchFlag:0},{qe:673,nmps:36,nlps:33,switchFlag:0},{qe:545,nmps:37,nlps:34,switchFlag:0},{qe:321,nmps:38,nlps:35,switchFlag:0},{qe:273,nmps:39,nlps:36,switchFlag:0},{qe:133,nmps:40,nlps:37,switchFlag:0},{qe:73,nmps:41,nlps:38,switchFlag:0},{qe:37,nmps:42,nlps:39,switchFlag:0},{qe:21,nmps:43,nlps:40,switchFlag:0},{qe:9,nmps:44,nlps:41,switchFlag:0},{qe:5,nmps:45,nlps:42,switchFlag:0},{qe:1,nmps:45,nlps:43,switchFlag:0},{qe:22017,nmps:46,nlps:46,switchFlag:0}];class ArithmeticDecoder{constructor(e,t,i){this.data=e;this.bp=t;this.dataEnd=i;this.chigh=e[t];this.clow=0;this.byteIn();this.chigh=this.chigh<<7&65535|this.clow>>9&127;this.clow=this.clow<<7&65535;this.ct-=7;this.a=32768}byteIn(){const e=this.data;let t=this.bp;if(255===e[t])if(e[t+1]>143){this.clow+=65280;this.ct=8}else{t++;this.clow+=e[t]<<9;this.ct=7;this.bp=t}else{t++;this.clow+=t<this.dataEnd?e[t]<<8:65280;this.ct=8;this.bp=t}if(this.clow>65535){this.chigh+=this.clow>>16;this.clow&=65535}}readBit(e,t){let i=e[t]>>1,a=1&e[t];const s=Wt[i],r=s.qe;let n,g=this.a-r;if(this.chigh<r)if(g<r){g=r;n=a;i=s.nmps}else{g=r;n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{this.chigh-=r;if(0!=(32768&g)){this.a=g;return a}if(g<r){n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{n=a;i=s.nmps}}do{0===this.ct&&this.byteIn();g<<=1;this.chigh=this.chigh<<1&65535|this.clow>>15&1;this.clow=this.clow<<1&65535;this.ct--}while(0==(32768&g));this.a=g;e[t]=i<<1|a;return n}}class Jbig2Error extends ot{constructor(e){super(e,"Jbig2Error")}}class ContextCache{getContexts(e){return e in this?this[e]:this[e]=new Int8Array(65536)}}class DecodingContext{constructor(e,t,i){this.data=e;this.start=t;this.end=i}get decoder(){return shadow(this,"decoder",new ArithmeticDecoder(this.data,this.start,this.end))}get contextCache(){return shadow(this,"contextCache",new ContextCache)}}const jt=2**31-1,Xt=-(2**31);function decodeInteger(e,t,i){const a=e.getContexts(t);let s=1;function readBits(e){let t=0;for(let r=0;r<e;r++){const e=i.readBit(a,s);s=s<256?s<<1|e:511&(s<<1|e)|256;t=t<<1|e}return t>>>0}const r=readBits(1),n=readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(32)+4436:readBits(12)+340:readBits(8)+84:readBits(6)+20:readBits(4)+4:readBits(2);let g;0===r?g=n:n>0&&(g=-n);return g>=Xt&&g<=jt?g:null}function decodeIAID(e,t,i){const a=e.getContexts("IAID");let s=1;for(let e=0;e<i;e++){s=s<<1|t.readBit(a,s)}return i<31?s&(1<<i)-1:2147483647&s}const Zt=["SymbolDictionary",null,null,null,"IntermediateTextRegion",null,"ImmediateTextRegion","ImmediateLosslessTextRegion",null,null,null,null,null,null,null,null,"PatternDictionary",null,null,null,"IntermediateHalftoneRegion",null,"ImmediateHalftoneRegion","ImmediateLosslessHalftoneRegion",null,null,null,null,null,null,null,null,null,null,null,null,"IntermediateGenericRegion",null,"ImmediateGenericRegion","ImmediateLosslessGenericRegion","IntermediateGenericRefinementRegion",null,"ImmediateGenericRefinementRegion","ImmediateLosslessGenericRefinementRegion",null,null,null,null,"PageInformation","EndOfPage","EndOfStripe","EndOfFile","Profiles","Tables",null,null,null,null,null,null,null,null,"Extension"],Vt=[[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:2,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-2,y:0},{x:-1,y:0}],[{x:-3,y:-1},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}]],zt=[{coding:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}]},{coding:[{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:0,y:1},{x:1,y:1}]}],_t=[39717,1941,229,405],$t=[32,8];function decodeBitmap(e,t,i,a,s,r,n,g){if(e){return decodeMMRBitmap(new Reader(g.data,g.start,g.end),t,i,!1)}if(0===a&&!r&&!s&&4===n.length&&3===n[0].x&&-1===n[0].y&&-3===n[1].x&&-1===n[1].y&&2===n[2].x&&-2===n[2].y&&-2===n[3].x&&-2===n[3].y)return function decodeBitmapTemplate0(e,t,i){const a=i.decoder,s=i.contextCache.getContexts("GB"),r=[];let n,g,o,c,C,h,l;for(g=0;g<t;g++){C=r[g]=new Uint8Array(e);h=g<1?C:r[g-1];l=g<2?C:r[g-2];n=l[0]<<13|l[1]<<12|l[2]<<11|h[0]<<7|h[1]<<6|h[2]<<5|h[3]<<4;for(o=0;o<e;o++){C[o]=c=a.readBit(s,n);n=(31735&n)<<1|(o+3<e?l[o+3]<<11:0)|(o+4<e?h[o+4]<<4:0)|c}}return r}(t,i,g);const o=!!r,c=Vt[a].concat(n);c.sort((function(e,t){return e.y-t.y||e.x-t.x}));const C=c.length,h=new Int8Array(C),l=new Int8Array(C),Q=[];let E,u,d=0,f=0,p=0,m=0;for(u=0;u<C;u++){h[u]=c[u].x;l[u]=c[u].y;f=Math.min(f,c[u].x);p=Math.max(p,c[u].x);m=Math.min(m,c[u].y);u<C-1&&c[u].y===c[u+1].y&&c[u].x===c[u+1].x-1?d|=1<<C-1-u:Q.push(u)}const y=Q.length,w=new Int8Array(y),D=new Int8Array(y),b=new Uint16Array(y);for(E=0;E<y;E++){u=Q[E];w[E]=c[u].x;D[E]=c[u].y;b[E]=1<<C-1-u}const F=-f,S=-m,k=t-p,R=_t[a];let N=new Uint8Array(t);const G=[],x=g.decoder,U=g.contextCache.getContexts("GB");let M,L,H,J,Y,v=0,K=0;for(let e=0;e<i;e++){if(s){v^=x.readBit(U,R);if(v){G.push(N);continue}}N=new Uint8Array(N);G.push(N);for(M=0;M<t;M++){if(o&&r[e][M]){N[M]=0;continue}if(M>=F&&M<k&&e>=S){K=K<<1&d;for(u=0;u<y;u++){L=e+D[u];H=M+w[u];J=G[L][H];if(J){J=b[u];K|=J}}}else{K=0;Y=C-1;for(u=0;u<C;u++,Y--){H=M+h[u];if(H>=0&&H<t){L=e+l[u];if(L>=0){J=G[L][H];J&&(K|=J<<Y)}}}}const i=x.readBit(U,K);N[M]=i}}return G}function decodeRefinement(e,t,i,a,s,r,n,g,o){let c=zt[i].coding;0===i&&(c=c.concat([g[0]]));const C=c.length,h=new Int32Array(C),l=new Int32Array(C);let Q;for(Q=0;Q<C;Q++){h[Q]=c[Q].x;l[Q]=c[Q].y}let E=zt[i].reference;0===i&&(E=E.concat([g[1]]));const u=E.length,d=new Int32Array(u),f=new Int32Array(u);for(Q=0;Q<u;Q++){d[Q]=E[Q].x;f[Q]=E[Q].y}const p=a[0].length,m=a.length,y=$t[i],w=[],D=o.decoder,b=o.contextCache.getContexts("GR");let F=0;for(let i=0;i<t;i++){if(n){F^=D.readBit(b,y);if(F)throw new Jbig2Error("prediction is not supported")}const t=new Uint8Array(e);w.push(t);for(let n=0;n<e;n++){let g,o,c=0;for(Q=0;Q<C;Q++){g=i+l[Q];o=n+h[Q];g<0||o<0||o>=e?c<<=1:c=c<<1|w[g][o]}for(Q=0;Q<u;Q++){g=i+f[Q]-r;o=n+d[Q]-s;g<0||g>=m||o<0||o>=p?c<<=1:c=c<<1|a[g][o]}const E=D.readBit(b,c);t[n]=E}}return w}function decodeTextRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E,u,d,f,p){if(e&&t)throw new Jbig2Error("refinement with Huffman is not supported");const m=[];let y,w;for(y=0;y<a;y++){w=new Uint8Array(i);if(s)for(let e=0;e<i;e++)w[e]=s;m.push(w)}const D=d.decoder,b=d.contextCache;let F=e?-Q.tableDeltaT.decode(p):-decodeInteger(b,"IADT",D),S=0;y=0;for(;y<r;){F+=e?Q.tableDeltaT.decode(p):decodeInteger(b,"IADT",D);S+=e?Q.tableFirstS.decode(p):decodeInteger(b,"IAFS",D);let a=S;for(;;){let s=0;n>1&&(s=e?p.readBits(f):decodeInteger(b,"IAIT",D));const r=n*F+s,S=e?Q.symbolIDTable.decode(p):decodeIAID(b,D,o),k=t&&(e?p.readBit():decodeInteger(b,"IARI",D));let R=g[S],N=R[0].length,G=R.length;if(k){const e=decodeInteger(b,"IARDW",D),t=decodeInteger(b,"IARDH",D);N+=e;G+=t;R=decodeRefinement(N,G,E,R,(e>>1)+decodeInteger(b,"IARDX",D),(t>>1)+decodeInteger(b,"IARDY",D),!1,u,d)}let x=0;c?1&h?x=G-1:a+=G-1:h>1?a+=N-1:x=N-1;const U=r-(1&h?0:G-1),M=a-(2&h?N-1:0);let L,H,J;if(c)for(L=0;L<G;L++){w=m[M+L];if(!w)continue;J=R[L];const e=Math.min(i-U,N);switch(l){case 0:for(H=0;H<e;H++)w[U+H]|=J[H];break;case 2:for(H=0;H<e;H++)w[U+H]^=J[H];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}else for(H=0;H<G;H++){w=m[U+H];if(w){J=R[H];switch(l){case 0:for(L=0;L<N;L++)w[M+L]|=J[L];break;case 2:for(L=0;L<N;L++)w[M+L]^=J[L];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}}y++;const Y=e?Q.tableDeltaS.decode(p):decodeInteger(b,"IADS",D);if(null===Y)break;a+=x+Y+C}}return m}function readSegmentHeader(e,t){const i={};i.number=readUint32(e,t);const a=e[t+4],s=63&a;if(!Zt[s])throw new Jbig2Error("invalid segment type: "+s);i.type=s;i.typeName=Zt[s];i.deferredNonRetain=!!(128&a);const r=!!(64&a),n=e[t+5];let g=n>>5&7;const o=[31&n];let c=t+6;if(7===n){g=536870911&readUint32(e,c-1);c+=3;let t=g+7>>3;o[0]=e[c++];for(;--t>0;)o.push(e[c++])}else if(5===n||6===n)throw new Jbig2Error("invalid referred-to flags");i.retainBits=o;let C=4;i.number<=256?C=1:i.number<=65536&&(C=2);const h=[];let l,Q;for(l=0;l<g;l++){let t;t=1===C?e[c]:2===C?readUint16(e,c):readUint32(e,c);h.push(t);c+=C}i.referredTo=h;if(r){i.pageAssociation=readUint32(e,c);c+=4}else i.pageAssociation=e[c++];i.length=readUint32(e,c);c+=4;if(4294967295===i.length){if(38!==s)throw new Jbig2Error("invalid unknown segment length");{const t=readRegionSegmentInformation(e,c),a=!!(1&e[c+Ai]),s=6,r=new Uint8Array(s);if(!a){r[0]=255;r[1]=172}r[2]=t.height>>>24&255;r[3]=t.height>>16&255;r[4]=t.height>>8&255;r[5]=255&t.height;for(l=c,Q=e.length;l<Q;l++){let t=0;for(;t<s&&r[t]===e[l+t];)t++;if(t===s){i.length=l+s;break}}if(4294967295===i.length)throw new Jbig2Error("segment end was not found")}}i.headerEnd=c;return i}function readSegments(e,t,i,a){const s=[];let r=i;for(;r<a;){const i=readSegmentHeader(t,r);r=i.headerEnd;const a={header:i,data:t};if(!e.randomAccess){a.start=r;r+=i.length;a.end=r}s.push(a);if(51===i.type)break}if(e.randomAccess)for(let e=0,t=s.length;e<t;e++){s[e].start=r;r+=s[e].header.length;s[e].end=r}return s}function readRegionSegmentInformation(e,t){return{width:readUint32(e,t),height:readUint32(e,t+4),x:readUint32(e,t+8),y:readUint32(e,t+12),combinationOperator:7&e[t+16]}}const Ai=17;function processSegment(e,t){const i=e.header,a=e.data,s=e.end;let r,n,g,o,c=e.start;switch(i.type){case 0:const e={},t=readUint16(a,c);e.huffman=!!(1&t);e.refinement=!!(2&t);e.huffmanDHSelector=t>>2&3;e.huffmanDWSelector=t>>4&3;e.bitmapSizeSelector=t>>6&1;e.aggregationInstancesSelector=t>>7&1;e.bitmapCodingContextUsed=!!(256&t);e.bitmapCodingContextRetained=!!(512&t);e.template=t>>10&3;e.refinementTemplate=t>>12&1;c+=2;if(!e.huffman){o=0===e.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.at=n}if(e.refinement&&!e.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.refinementAt=n}e.numberOfExportedSymbols=readUint32(a,c);c+=4;e.numberOfNewSymbols=readUint32(a,c);c+=4;r=[e,i.number,i.referredTo,a,c,s];break;case 6:case 7:const C={};C.info=readRegionSegmentInformation(a,c);c+=Ai;const h=readUint16(a,c);c+=2;C.huffman=!!(1&h);C.refinement=!!(2&h);C.logStripSize=h>>2&3;C.stripSize=1<<C.logStripSize;C.referenceCorner=h>>4&3;C.transposed=!!(64&h);C.combinationOperator=h>>7&3;C.defaultPixelValue=h>>9&1;C.dsOffset=h<<17>>27;C.refinementTemplate=h>>15&1;if(C.huffman){const e=readUint16(a,c);c+=2;C.huffmanFS=3&e;C.huffmanDS=e>>2&3;C.huffmanDT=e>>4&3;C.huffmanRefinementDW=e>>6&3;C.huffmanRefinementDH=e>>8&3;C.huffmanRefinementDX=e>>10&3;C.huffmanRefinementDY=e>>12&3;C.huffmanRefinementSizeSelector=!!(16384&e)}if(C.refinement&&!C.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}C.refinementAt=n}C.numberOfSymbolInstances=readUint32(a,c);c+=4;r=[C,i.referredTo,a,c,s];break;case 16:const l={},Q=a[c++];l.mmr=!!(1&Q);l.template=Q>>1&3;l.patternWidth=a[c++];l.patternHeight=a[c++];l.maxPatternIndex=readUint32(a,c);c+=4;r=[l,i.number,a,c,s];break;case 22:case 23:const E={};E.info=readRegionSegmentInformation(a,c);c+=Ai;const u=a[c++];E.mmr=!!(1&u);E.template=u>>1&3;E.enableSkip=!!(8&u);E.combinationOperator=u>>4&7;E.defaultPixelValue=u>>7&1;E.gridWidth=readUint32(a,c);c+=4;E.gridHeight=readUint32(a,c);c+=4;E.gridOffsetX=4294967295&readUint32(a,c);c+=4;E.gridOffsetY=4294967295&readUint32(a,c);c+=4;E.gridVectorX=readUint16(a,c);c+=2;E.gridVectorY=readUint16(a,c);c+=2;r=[E,i.referredTo,a,c,s];break;case 38:case 39:const d={};d.info=readRegionSegmentInformation(a,c);c+=Ai;const f=a[c++];d.mmr=!!(1&f);d.template=f>>1&3;d.prediction=!!(8&f);if(!d.mmr){o=0===d.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}d.at=n}r=[d,a,c,s];break;case 48:const p={width:readUint32(a,c),height:readUint32(a,c+4),resolutionX:readUint32(a,c+8),resolutionY:readUint32(a,c+12)};4294967295===p.height&&delete p.height;const m=a[c+16];readUint16(a,c+17);p.lossless=!!(1&m);p.refinement=!!(2&m);p.defaultPixelValue=m>>2&1;p.combinationOperator=m>>3&3;p.requiresBuffer=!!(32&m);p.combinationOperatorOverride=!!(64&m);r=[p];break;case 49:case 50:case 51:case 62:break;case 53:r=[i.number,a,c,s];break;default:throw new Jbig2Error(`segment type ${i.typeName}(${i.type}) is not implemented`)}const C="on"+i.typeName;C in t&&t[C].apply(t,r)}function processSegments(e,t){for(let i=0,a=e.length;i<a;i++)processSegment(e[i],t)}class SimpleSegmentVisitor{onPageInformation(e){this.currentPageInfo=e;const t=e.width+7>>3,i=new Uint8ClampedArray(t*e.height);e.defaultPixelValue&&i.fill(255);this.buffer=i}drawBitmap(e,t){const i=this.currentPageInfo,a=e.width,s=e.height,r=i.width+7>>3,n=i.combinationOperatorOverride?e.combinationOperator:i.combinationOperator,g=this.buffer,o=128>>(7&e.x);let c,C,h,l,Q=e.y*r+(e.x>>3);switch(n){case 0:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]|=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;case 2:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]^=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;default:throw new Jbig2Error(`operator ${n} is not supported`)}}onImmediateGenericRegion(e,t,i,a){const s=e.info,r=new DecodingContext(t,i,a),n=decodeBitmap(e.mmr,s.width,s.height,e.template,e.prediction,null,e.at,r);this.drawBitmap(s,n)}onImmediateLosslessGenericRegion(){this.onImmediateGenericRegion(...arguments)}onSymbolDictionary(e,t,i,a,s,r){let n,g;if(e.huffman){n=function getSymbolDictionaryHuffmanTables(e,t,i){let a,s,r,n,g=0;switch(e.huffmanDHSelector){case 0:case 1:a=getStandardTable(e.huffmanDHSelector+4);break;case 3:a=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DH selector")}switch(e.huffmanDWSelector){case 0:case 1:s=getStandardTable(e.huffmanDWSelector+2);break;case 3:s=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DW selector")}if(e.bitmapSizeSelector){r=getCustomHuffmanTable(g,t,i);g++}else r=getStandardTable(1);n=e.aggregationInstancesSelector?getCustomHuffmanTable(g,t,i):getStandardTable(1);return{tableDeltaHeight:a,tableDeltaWidth:s,tableBitmapSize:r,tableAggregateInstances:n}}(e,i,this.customTables);g=new Reader(a,s,r)}let o=this.symbols;o||(this.symbols=o={});const c=[];for(const e of i){const t=o[e];t&&c.push(...t)}const C=new DecodingContext(a,s,r);o[t]=function decodeSymbolDictionary(e,t,i,a,s,r,n,g,o,c,C,h){if(e&&t)throw new Jbig2Error("symbol refinement with Huffman is not supported");const l=[];let Q=0,E=log2(i.length+a);const u=C.decoder,d=C.contextCache;let f,p;if(e){f=getStandardTable(1);p=[];E=Math.max(E,1)}for(;l.length<a;){Q+=e?r.tableDeltaHeight.decode(h):decodeInteger(d,"IADH",u);let a=0,s=0;const f=e?p.length:0;for(;;){const f=e?r.tableDeltaWidth.decode(h):decodeInteger(d,"IADW",u);if(null===f)break;a+=f;s+=a;let m;if(t){const s=decodeInteger(d,"IAAI",u);if(s>1)m=decodeTextRegion(e,t,a,Q,0,s,1,i.concat(l),E,0,0,1,0,r,o,c,C,0,h);else{const e=decodeIAID(d,u,E),t=decodeInteger(d,"IARDX",u),s=decodeInteger(d,"IARDY",u);m=decodeRefinement(a,Q,o,e<i.length?i[e]:l[e-i.length],t,s,!1,c,C)}l.push(m)}else if(e)p.push(a);else{m=decodeBitmap(!1,a,Q,n,!1,null,g,C);l.push(m)}}if(e&&!t){const e=r.tableBitmapSize.decode(h);h.byteAlign();let t;if(0===e)t=readUncompressedBitmap(h,s,Q);else{const i=h.end,a=h.position+e;h.end=a;t=decodeMMRBitmap(h,s,Q,!1);h.end=i;h.position=a}const i=p.length;if(f===i-1)l.push(t);else{let e,a,s,r,n,g=0;for(e=f;e<i;e++){r=p[e];s=g+r;n=[];for(a=0;a<Q;a++)n.push(t[a].subarray(g,s));l.push(n);g=s}}}}const m=[],y=[];let w,D,b=!1;const F=i.length+a;for(;y.length<F;){let t=e?f.decode(h):decodeInteger(d,"IAEX",u);for(;t--;)y.push(b);b=!b}for(w=0,D=i.length;w<D;w++)y[w]&&m.push(i[w]);for(let e=0;e<a;w++,e++)y[w]&&m.push(l[e]);return m}(e.huffman,e.refinement,c,e.numberOfNewSymbols,e.numberOfExportedSymbols,n,e.template,e.at,e.refinementTemplate,e.refinementAt,C,g)}onImmediateTextRegion(e,t,i,a,s){const r=e.info;let n,g;const o=this.symbols,c=[];for(const e of t){const t=o[e];t&&c.push(...t)}const C=log2(c.length);if(e.huffman){g=new Reader(i,a,s);n=function getTextRegionHuffmanTables(e,t,i,a,s){const r=[];for(let e=0;e<=34;e++){const t=s.readBits(4);r.push(new HuffmanLine([e,t,0,0]))}const n=new HuffmanTable(r,!1);r.length=0;for(let e=0;e<a;){const t=n.decode(s);if(t>=32){let i,a,n;switch(t){case 32:if(0===e)throw new Jbig2Error("no previous value in symbol ID table");a=s.readBits(2)+3;i=r[e-1].prefixLength;break;case 33:a=s.readBits(3)+3;i=0;break;case 34:a=s.readBits(7)+11;i=0;break;default:throw new Jbig2Error("invalid code length in symbol ID table")}for(n=0;n<a;n++){r.push(new HuffmanLine([e,i,0,0]));e++}}else{r.push(new HuffmanLine([e,t,0,0]));e++}}s.byteAlign();const g=new HuffmanTable(r,!1);let o,c,C,h=0;switch(e.huffmanFS){case 0:case 1:o=getStandardTable(e.huffmanFS+6);break;case 3:o=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman FS selector")}switch(e.huffmanDS){case 0:case 1:case 2:c=getStandardTable(e.huffmanDS+8);break;case 3:c=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DS selector")}switch(e.huffmanDT){case 0:case 1:case 2:C=getStandardTable(e.huffmanDT+11);break;case 3:C=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DT selector")}if(e.refinement)throw new Jbig2Error("refinement with Huffman is not supported");return{symbolIDTable:g,tableFirstS:o,tableDeltaS:c,tableDeltaT:C}}(e,t,this.customTables,c.length,g)}const h=new DecodingContext(i,a,s),l=decodeTextRegion(e.huffman,e.refinement,r.width,r.height,e.defaultPixelValue,e.numberOfSymbolInstances,e.stripSize,c,C,e.transposed,e.dsOffset,e.referenceCorner,e.combinationOperator,n,e.refinementTemplate,e.refinementAt,h,e.logStripSize,g);this.drawBitmap(r,l)}onImmediateLosslessTextRegion(){this.onImmediateTextRegion(...arguments)}onPatternDictionary(e,t,i,a,s){let r=this.patterns;r||(this.patterns=r={});const n=new DecodingContext(i,a,s);r[t]=function decodePatternDictionary(e,t,i,a,s,r){const n=[];if(!e){n.push({x:-t,y:0});0===s&&n.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const g=decodeBitmap(e,(a+1)*t,i,s,!1,null,n,r),o=[];for(let e=0;e<=a;e++){const a=[],s=t*e,r=s+t;for(let e=0;e<i;e++)a.push(g[e].subarray(s,r));o.push(a)}return o}(e.mmr,e.patternWidth,e.patternHeight,e.maxPatternIndex,e.template,n)}onImmediateHalftoneRegion(e,t,i,a,s){const r=this.patterns[t[0]],n=e.info,g=new DecodingContext(i,a,s),o=function decodeHalftoneRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E){if(n)throw new Jbig2Error("skip is not supported");if(0!==g)throw new Jbig2Error(`operator "${g}" is not supported in halftone region`);const u=[];let d,f,p;for(d=0;d<s;d++){p=new Uint8Array(a);if(r)for(f=0;f<a;f++)p[f]=r;u.push(p)}const m=t.length,y=t[0],w=y[0].length,D=y.length,b=log2(m),F=[];if(!e){F.push({x:i<=1?3:2,y:-1});0===i&&F.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const S=[];let k,R,N,G,x,U,M,L,H,J,Y;e&&(k=new Reader(E.data,E.start,E.end));for(d=b-1;d>=0;d--){R=e?decodeMMRBitmap(k,o,c,!0):decodeBitmap(!1,o,c,i,!1,null,F,E);S[d]=R}for(N=0;N<c;N++)for(G=0;G<o;G++){x=0;U=0;for(f=b-1;f>=0;f--){x^=S[f][N][G];U|=x<<f}M=t[U];L=C+N*Q+G*l>>8;H=h+N*l-G*Q>>8;if(L>=0&&L+w<=a&&H>=0&&H+D<=s)for(d=0;d<D;d++){Y=u[H+d];J=M[d];for(f=0;f<w;f++)Y[L+f]|=J[f]}else{let e,t;for(d=0;d<D;d++){t=H+d;if(!(t<0||t>=s)){Y=u[t];J=M[d];for(f=0;f<w;f++){e=L+f;e>=0&&e<a&&(Y[e]|=J[f])}}}}}return u}(e.mmr,r,e.template,n.width,n.height,e.defaultPixelValue,e.enableSkip,e.combinationOperator,e.gridWidth,e.gridHeight,e.gridOffsetX,e.gridOffsetY,e.gridVectorX,e.gridVectorY,g);this.drawBitmap(n,o)}onImmediateLosslessHalftoneRegion(){this.onImmediateHalftoneRegion(...arguments)}onTables(e,t,i,a){let s=this.customTables;s||(this.customTables=s={});s[e]=function decodeTablesSegment(e,t,i){const a=e[t],s=4294967295&readUint32(e,t+1),r=4294967295&readUint32(e,t+5),n=new Reader(e,t+9,i),g=1+(a>>1&7),o=1+(a>>4&7),c=[];let C,h,l=s;do{C=n.readBits(g);h=n.readBits(o);c.push(new HuffmanLine([l,C,h,0]));l+=1<<h}while(l<r);C=n.readBits(g);c.push(new HuffmanLine([s-1,C,32,0,"lower"]));C=n.readBits(g);c.push(new HuffmanLine([r,C,32,0]));if(1&a){C=n.readBits(g);c.push(new HuffmanLine([C,0]))}return new HuffmanTable(c,!1)}(t,i,a)}}class HuffmanLine{constructor(e){if(2===e.length){this.isOOB=!0;this.rangeLow=0;this.prefixLength=e[0];this.rangeLength=0;this.prefixCode=e[1];this.isLowerRange=!1}else{this.isOOB=!1;this.rangeLow=e[0];this.prefixLength=e[1];this.rangeLength=e[2];this.prefixCode=e[3];this.isLowerRange="lower"===e[4]}}}class HuffmanTreeNode{constructor(e){this.children=[];if(e){this.isLeaf=!0;this.rangeLength=e.rangeLength;this.rangeLow=e.rangeLow;this.isLowerRange=e.isLowerRange;this.isOOB=e.isOOB}else this.isLeaf=!1}buildTree(e,t){const i=e.prefixCode>>t&1;if(t<=0)this.children[i]=new HuffmanTreeNode(e);else{let a=this.children[i];a||(this.children[i]=a=new HuffmanTreeNode(null));a.buildTree(e,t-1)}}decodeNode(e){if(this.isLeaf){if(this.isOOB)return null;const t=e.readBits(this.rangeLength);return this.rangeLow+(this.isLowerRange?-t:t)}const t=this.children[e.readBit()];if(!t)throw new Jbig2Error("invalid Huffman data");return t.decodeNode(e)}}class HuffmanTable{constructor(e,t){t||this.assignPrefixCodes(e);this.rootNode=new HuffmanTreeNode(null);for(let t=0,i=e.length;t<i;t++){const i=e[t];i.prefixLength>0&&this.rootNode.buildTree(i,i.prefixLength-1)}}decode(e){return this.rootNode.decodeNode(e)}assignPrefixCodes(e){const t=e.length;let i=0;for(let a=0;a<t;a++)i=Math.max(i,e[a].prefixLength);const a=new Uint32Array(i+1);for(let i=0;i<t;i++)a[e[i].prefixLength]++;let s,r,n,g=1,o=0;a[0]=0;for(;g<=i;){o=o+a[g-1]<<1;s=o;r=0;for(;r<t;){n=e[r];if(n.prefixLength===g){n.prefixCode=s;s++}r++}g++}}}const ei={};function getStandardTable(e){let t,i=ei[e];if(i)return i;switch(e){case 1:t=[[0,1,4,0],[16,2,8,2],[272,3,16,6],[65808,3,32,7]];break;case 2:t=[[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[75,6,32,62],[6,63]];break;case 3:t=[[-256,8,8,254],[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[-257,8,32,255,"lower"],[75,7,32,126],[6,62]];break;case 4:t=[[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[76,5,32,31]];break;case 5:t=[[-255,7,8,126],[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[-256,7,32,127,"lower"],[76,6,32,62]];break;case 6:t=[[-2048,5,10,28],[-1024,4,9,8],[-512,4,8,9],[-256,4,7,10],[-128,5,6,29],[-64,5,5,30],[-32,4,5,11],[0,2,7,0],[128,3,7,2],[256,3,8,3],[512,4,9,12],[1024,4,10,13],[-2049,6,32,62,"lower"],[2048,6,32,63]];break;case 7:t=[[-1024,4,9,8],[-512,3,8,0],[-256,4,7,9],[-128,5,6,26],[-64,5,5,27],[-32,4,5,10],[0,4,5,11],[32,5,5,28],[64,5,6,29],[128,4,7,12],[256,3,8,1],[512,3,9,2],[1024,3,10,3],[-1025,5,32,30,"lower"],[2048,5,32,31]];break;case 8:t=[[-15,8,3,252],[-7,9,1,508],[-5,8,1,253],[-3,9,0,509],[-2,7,0,124],[-1,4,0,10],[0,2,1,0],[2,5,0,26],[3,6,0,58],[4,3,4,4],[20,6,1,59],[22,4,4,11],[38,4,5,12],[70,5,6,27],[134,5,7,28],[262,6,7,60],[390,7,8,125],[646,6,10,61],[-16,9,32,510,"lower"],[1670,9,32,511],[2,1]];break;case 9:t=[[-31,8,4,252],[-15,9,2,508],[-11,8,2,253],[-7,9,1,509],[-5,7,1,124],[-3,4,1,10],[-1,3,1,2],[1,3,1,3],[3,5,1,26],[5,6,1,58],[7,3,5,4],[39,6,2,59],[43,4,5,11],[75,4,6,12],[139,5,7,27],[267,5,8,28],[523,6,8,60],[779,7,9,125],[1291,6,11,61],[-32,9,32,510,"lower"],[3339,9,32,511],[2,0]];break;case 10:t=[[-21,7,4,122],[-5,8,0,252],[-4,7,0,123],[-3,5,0,24],[-2,2,2,0],[2,5,0,25],[3,6,0,54],[4,7,0,124],[5,8,0,253],[6,2,6,1],[70,5,5,26],[102,6,5,55],[134,6,6,56],[198,6,7,57],[326,6,8,58],[582,6,9,59],[1094,6,10,60],[2118,7,11,125],[-22,8,32,254,"lower"],[4166,8,32,255],[2,2]];break;case 11:t=[[1,1,0,0],[2,2,1,2],[4,4,0,12],[5,4,1,13],[7,5,1,28],[9,5,2,29],[13,6,2,60],[17,7,2,122],[21,7,3,123],[29,7,4,124],[45,7,5,125],[77,7,6,126],[141,7,32,127]];break;case 12:t=[[1,1,0,0],[2,2,0,2],[3,3,1,6],[5,5,0,28],[6,5,1,29],[8,6,1,60],[10,7,0,122],[11,7,1,123],[13,7,2,124],[17,7,3,125],[25,7,4,126],[41,8,5,254],[73,8,32,255]];break;case 13:t=[[1,1,0,0],[2,3,0,4],[3,4,0,12],[4,5,0,28],[5,4,1,13],[7,3,3,5],[15,6,1,58],[17,6,2,59],[21,6,3,60],[29,6,4,61],[45,6,5,62],[77,7,6,126],[141,7,32,127]];break;case 14:t=[[-2,3,0,4],[-1,3,0,5],[0,1,0,0],[1,3,0,6],[2,3,0,7]];break;case 15:t=[[-24,7,4,124],[-8,6,2,60],[-4,5,1,28],[-2,4,0,12],[-1,3,0,4],[0,1,0,0],[1,3,0,5],[2,4,0,13],[3,5,1,29],[5,6,2,61],[9,7,4,125],[-25,7,32,126,"lower"],[25,7,32,127]];break;default:throw new Jbig2Error(`standard table B.${e} does not exist`)}for(let e=0,i=t.length;e<i;e++)t[e]=new HuffmanLine(t[e]);i=new HuffmanTable(t,!0);ei[e]=i;return i}class Reader{constructor(e,t,i){this.data=e;this.start=t;this.end=i;this.position=t;this.shift=-1;this.currentByte=0}readBit(){if(this.shift<0){if(this.position>=this.end)throw new Jbig2Error("end of data while reading bit");this.currentByte=this.data[this.position++];this.shift=7}const e=this.currentByte>>this.shift&1;this.shift--;return e}readBits(e){let t,i=0;for(t=e-1;t>=0;t--)i|=this.readBit()<<t;return i}byteAlign(){this.shift=-1}next(){return this.position>=this.end?-1:this.data[this.position++]}}function getCustomHuffmanTable(e,t,i){let a=0;for(let s=0,r=t.length;s<r;s++){const r=i[t[s]];if(r){if(e===a)return r;a++}}throw new Jbig2Error("can't find custom Huffman table")}function readUncompressedBitmap(e,t,i){const a=[];for(let s=0;s<i;s++){const i=new Uint8Array(t);a.push(i);for(let a=0;a<t;a++)i[a]=e.readBit();e.byteAlign()}return a}function decodeMMRBitmap(e,t,i,a){const s=new CCITTFaxDecoder(e,{K:-1,Columns:t,Rows:i,BlackIs1:!0,EndOfBlock:a}),r=[];let n,g=!1;for(let e=0;e<i;e++){const e=new Uint8Array(t);r.push(e);let i=-1;for(let a=0;a<t;a++){if(i<0){n=s.readNextChar();if(-1===n){n=0;g=!0}i=7}e[a]=n>>i&1;i--}}if(a&&!g){const e=5;for(let t=0;t<e&&-1!==s.readNextChar();t++);}return r}class Jbig2Image{parseChunks(e){return function parseJbig2Chunks(e){const t=new SimpleSegmentVisitor;for(let i=0,a=e.length;i<a;i++){const a=e[i];processSegments(readSegments({},a.data,a.start,a.end),t)}return t.buffer}(e)}parse(e){throw new Error("Not implemented: Jbig2Image.parse")}}class Jbig2Stream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}decodeImage(e){if(this.eof)return this.buffer;e||=this.bytes;const t=new Jbig2Image,i=[];if(this.params instanceof Dict){const e=this.params.get("JBIG2Globals");if(e instanceof BaseStream){const t=e.getBytes();i.push({data:t,start:0,end:t.length})}}i.push({data:e,start:0,end:e.length});const a=t.parseChunks(i),s=a.length;for(let e=0;e<s;e++)a[e]^=255;this.buffer=a;this.bufferLength=s;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}function convertToRGBA(e){switch(e.kind){case b:return convertBlackAndWhiteToRGBA(e);case F:return function convertRGBToRGBA({src:e,srcPos:t=0,dest:i,destPos:a=0,width:s,height:r}){let n=0;const g=e.length>>2,o=new Uint32Array(e.buffer,t,g);if(FeatureTest.isLittleEndian){for(;n<g-2;n+=3,a+=4){const e=o[n],t=o[n+1],s=o[n+2];i[a]=4278190080|e;i[a+1]=e>>>24|t<<8|4278190080;i[a+2]=t>>>16|s<<16|4278190080;i[a+3]=s>>>8|4278190080}for(let t=4*n,s=e.length;t<s;t+=3)i[a++]=e[t]|e[t+1]<<8|e[t+2]<<16|4278190080}else{for(;n<g-2;n+=3,a+=4){const e=o[n],t=o[n+1],s=o[n+2];i[a]=255|e;i[a+1]=e<<24|t>>>8|255;i[a+2]=t<<16|s>>>16|255;i[a+3]=s<<8|255}for(let t=4*n,s=e.length;t<s;t+=3)i[a++]=e[t]<<24|e[t+1]<<16|e[t+2]<<8|255}return{srcPos:t,destPos:a}}(e)}return null}function convertBlackAndWhiteToRGBA({src:e,srcPos:t=0,dest:i,width:a,height:s,nonBlackColor:r=4294967295,inverseDecode:n=!1}){const g=FeatureTest.isLittleEndian?4278190080:255,[o,c]=n?[r,g]:[g,r],C=a>>3,h=7&a,l=e.length;i=new Uint32Array(i.buffer);let Q=0;for(let a=0;a<s;a++){for(const a=t+C;t<a;t++){const a=t<l?e[t]:255;i[Q++]=128&a?c:o;i[Q++]=64&a?c:o;i[Q++]=32&a?c:o;i[Q++]=16&a?c:o;i[Q++]=8&a?c:o;i[Q++]=4&a?c:o;i[Q++]=2&a?c:o;i[Q++]=1&a?c:o}if(0===h)continue;const a=t<l?e[t++]:255;for(let e=0;e<h;e++)i[Q++]=a&1<<7-e?c:o}return{srcPos:t,destPos:Q}}class JpegError extends ot{constructor(e){super(e,"JpegError")}}class DNLMarkerError extends ot{constructor(e,t){super(e,"DNLMarkerError");this.scanLines=t}}class EOIMarkerError extends ot{constructor(e){super(e,"EOIMarkerError")}}const ti=new Uint8Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]),ii=4017,ai=799,si=3406,ri=2276,ni=1567,gi=3784,oi=5793,Ii=2896;function buildHuffmanTable(e,t){let i,a,s=0,r=16;for(;r>0&&!e[r-1];)r--;const n=[{children:[],index:0}];let g,o=n[0];for(i=0;i<r;i++){for(a=0;a<e[i];a++){o=n.pop();o.children[o.index]=t[s];for(;o.index>0;)o=n.pop();o.index++;n.push(o);for(;n.length<=i;){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}s++}if(i+1<r){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}}return n[0].children}function getBlockBufferOffset(e,t,i){return 64*((e.blocksPerLine+1)*t+i)}function decodeScan(e,t,i,a,s,r,n,g,o,c=!1){const C=i.mcusPerLine,h=i.progressive,l=t;let Q=0,E=0;function readBit(){if(E>0){E--;return Q>>E&1}Q=e[t++];if(255===Q){const a=e[t++];if(a){if(220===a&&c){const a=readUint16(e,t+=2);t+=2;if(a>0&&a!==i.scanLines)throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data",a)}else if(217===a){if(c){const e=p*(8===i.precision?8:0);if(e>0&&Math.round(i.scanLines/e)>=5)throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, possibly caused by incorrect `scanLines` parameter",e)}throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data")}throw new JpegError(`unexpected marker ${(Q<<8|a).toString(16)}`)}}E=7;return Q>>>7}function decodeHuffman(e){let t=e;for(;;){t=t[readBit()];switch(typeof t){case"number":return t;case"object":continue}throw new JpegError("invalid huffman sequence")}}function receive(e){let t=0;for(;e>0;){t=t<<1|readBit();e--}return t}function receiveAndExtend(e){if(1===e)return 1===readBit()?1:-1;const t=receive(e);return t>=1<<e-1?t:t+(-1<<e)+1}let u=0;let d,f=0;let p=0;function decodeMcu(e,t,i,a,s){const r=i%C;p=(i/C|0)*e.v+a;const n=r*e.h+s;t(e,getBlockBufferOffset(e,p,n))}function decodeBlock(e,t,i){p=i/e.blocksPerLine|0;const a=i%e.blocksPerLine;t(e,getBlockBufferOffset(e,p,a))}const m=a.length;let y,w,D,b,F,S;S=h?0===r?0===g?function decodeDCFirst(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i)<<o;e.blockData[t]=e.pred+=a}:function decodeDCSuccessive(e,t){e.blockData[t]|=readBit()<<o}:0===g?function decodeACFirst(e,t){if(u>0){u--;return}let i=r;const a=n;for(;i<=a;){const a=decodeHuffman(e.huffmanTableAC),s=15&a,r=a>>4;if(0===s){if(r<15){u=receive(r)+(1<<r)-1;break}i+=16;continue}i+=r;const n=ti[i];e.blockData[t+n]=receiveAndExtend(s)*(1<<o);i++}}:function decodeACSuccessive(e,t){let i=r;const a=n;let s,g,c=0;for(;i<=a;){const a=t+ti[i],r=e.blockData[a]<0?-1:1;switch(f){case 0:g=decodeHuffman(e.huffmanTableAC);s=15&g;c=g>>4;if(0===s)if(c<15){u=receive(c)+(1<<c);f=4}else{c=16;f=1}else{if(1!==s)throw new JpegError("invalid ACn encoding");d=receiveAndExtend(s);f=c?2:3}continue;case 1:case 2:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{c--;0===c&&(f=2===f?3:0)}break;case 3:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{e.blockData[a]=d<<o;f=0}break;case 4:e.blockData[a]&&(e.blockData[a]+=r*(readBit()<<o))}i++}if(4===f){u--;0===u&&(f=0)}}:function decodeBaseline(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i);e.blockData[t]=e.pred+=a;let s=1;for(;s<64;){const i=decodeHuffman(e.huffmanTableAC),a=15&i,r=i>>4;if(0===a){if(r<15)break;s+=16;continue}s+=r;const n=ti[s];e.blockData[t+n]=receiveAndExtend(a);s++}};let k,R=0;const N=1===m?a[0].blocksPerLine*a[0].blocksPerColumn:C*i.mcusPerColumn;let G,x;for(;R<=N;){const i=s?Math.min(N-R,s):N;if(i>0){for(w=0;w<m;w++)a[w].pred=0;u=0;if(1===m){y=a[0];for(F=0;F<i;F++){decodeBlock(y,S,R);R++}}else for(F=0;F<i;F++){for(w=0;w<m;w++){y=a[w];G=y.h;x=y.v;for(D=0;D<x;D++)for(b=0;b<G;b++)decodeMcu(y,S,R,D,b)}R++}}E=0;k=findNextFileMarker(e,t);if(!k)break;if(k.invalid){warn(`decodeScan - ${i>0?"unexpected":"excessive"} MCU data, current marker is: ${k.invalid}`);t=k.offset}if(!(k.marker>=65488&&k.marker<=65495))break;t+=2}return t-l}function quantizeAndInverse(e,t,i){const a=e.quantizationTable,s=e.blockData;let r,n,g,o,c,C,h,l,Q,E,u,d,f,p,m,y,w;if(!a)throw new JpegError("missing required Quantization Table.");for(let e=0;e<64;e+=8){Q=s[t+e];E=s[t+e+1];u=s[t+e+2];d=s[t+e+3];f=s[t+e+4];p=s[t+e+5];m=s[t+e+6];y=s[t+e+7];Q*=a[e];if(0!=(E|u|d|f|p|m|y)){E*=a[e+1];u*=a[e+2];d*=a[e+3];f*=a[e+4];p*=a[e+5];m*=a[e+6];y*=a[e+7];r=oi*Q+128>>8;n=oi*f+128>>8;g=u;o=m;c=Ii*(E-y)+128>>8;l=Ii*(E+y)+128>>8;C=d<<4;h=p<<4;r=r+n+1>>1;n=r-n;w=g*gi+o*ni+128>>8;g=g*ni-o*gi+128>>8;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*ri+l*si+2048>>12;c=c*si-l*ri+2048>>12;l=w;w=C*ai+h*ii+2048>>12;C=C*ii-h*ai+2048>>12;h=w;i[e]=r+l;i[e+7]=r-l;i[e+1]=n+h;i[e+6]=n-h;i[e+2]=g+C;i[e+5]=g-C;i[e+3]=o+c;i[e+4]=o-c}else{w=oi*Q+512>>10;i[e]=w;i[e+1]=w;i[e+2]=w;i[e+3]=w;i[e+4]=w;i[e+5]=w;i[e+6]=w;i[e+7]=w}}for(let e=0;e<8;++e){Q=i[e];E=i[e+8];u=i[e+16];d=i[e+24];f=i[e+32];p=i[e+40];m=i[e+48];y=i[e+56];if(0!=(E|u|d|f|p|m|y)){r=oi*Q+2048>>12;n=oi*f+2048>>12;g=u;o=m;c=Ii*(E-y)+2048>>12;l=Ii*(E+y)+2048>>12;C=d;h=p;r=4112+(r+n+1>>1);n=r-n;w=g*gi+o*ni+2048>>12;g=g*ni-o*gi+2048>>12;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*ri+l*si+2048>>12;c=c*si-l*ri+2048>>12;l=w;w=C*ai+h*ii+2048>>12;C=C*ii-h*ai+2048>>12;h=w;Q=r+l;y=r-l;E=n+h;m=n-h;u=g+C;p=g-C;d=o+c;f=o-c;Q<16?Q=0:Q>=4080?Q=255:Q>>=4;E<16?E=0:E>=4080?E=255:E>>=4;u<16?u=0:u>=4080?u=255:u>>=4;d<16?d=0:d>=4080?d=255:d>>=4;f<16?f=0:f>=4080?f=255:f>>=4;p<16?p=0:p>=4080?p=255:p>>=4;m<16?m=0:m>=4080?m=255:m>>=4;y<16?y=0:y>=4080?y=255:y>>=4;s[t+e]=Q;s[t+e+8]=E;s[t+e+16]=u;s[t+e+24]=d;s[t+e+32]=f;s[t+e+40]=p;s[t+e+48]=m;s[t+e+56]=y}else{w=oi*Q+8192>>14;w=w<-2040?0:w>=2024?255:w+2056>>4;s[t+e]=w;s[t+e+8]=w;s[t+e+16]=w;s[t+e+24]=w;s[t+e+32]=w;s[t+e+40]=w;s[t+e+48]=w;s[t+e+56]=w}}}function buildComponentData(e,t){const i=t.blocksPerLine,a=t.blocksPerColumn,s=new Int16Array(64);for(let e=0;e<a;e++)for(let a=0;a<i;a++){quantizeAndInverse(t,getBlockBufferOffset(t,e,a),s)}return t.blockData}function findNextFileMarker(e,t,i=t){const a=e.length-1;let s=i<t?i:t;if(t>=a)return null;const r=readUint16(e,t);if(r>=65472&&r<=65534)return{invalid:null,marker:r,offset:t};let n=readUint16(e,s);for(;!(n>=65472&&n<=65534);){if(++s>=a)return null;n=readUint16(e,s)}return{invalid:r.toString(16),marker:n,offset:s}}class JpegImage{constructor({decodeTransform:e=null,colorTransform:t=-1}={}){this._decodeTransform=e;this._colorTransform=t}parse(e,{dnlScanLines:t=null}={}){function readDataBlock(){const t=readUint16(e,s);s+=2;let i=s+t-2;const a=findNextFileMarker(e,i,s);if(a?.invalid){warn("readDataBlock - incorrect length, current marker is: "+a.invalid);i=a.offset}const r=e.subarray(s,i);s+=r.length;return r}function prepareComponents(e){const t=Math.ceil(e.samplesPerLine/8/e.maxH),i=Math.ceil(e.scanLines/8/e.maxV);for(const a of e.components){const s=Math.ceil(Math.ceil(e.samplesPerLine/8)*a.h/e.maxH),r=Math.ceil(Math.ceil(e.scanLines/8)*a.v/e.maxV),n=t*a.h,g=64*(i*a.v)*(n+1);a.blockData=new Int16Array(g);a.blocksPerLine=s;a.blocksPerColumn=r}e.mcusPerLine=t;e.mcusPerColumn=i}let i,a,s=0,r=null,n=null,g=0;const o=[],c=[],C=[];let h=readUint16(e,s);s+=2;if(65496!==h)throw new JpegError("SOI not found");h=readUint16(e,s);s+=2;A:for(;65497!==h;){let l,Q,E;switch(h){case 65504:case 65505:case 65506:case 65507:case 65508:case 65509:case 65510:case 65511:case 65512:case 65513:case 65514:case 65515:case 65516:case 65517:case 65518:case 65519:case 65534:const u=readDataBlock();65504===h&&74===u[0]&&70===u[1]&&73===u[2]&&70===u[3]&&0===u[4]&&(r={version:{major:u[5],minor:u[6]},densityUnits:u[7],xDensity:u[8]<<8|u[9],yDensity:u[10]<<8|u[11],thumbWidth:u[12],thumbHeight:u[13],thumbData:u.subarray(14,14+3*u[12]*u[13])});65518===h&&65===u[0]&&100===u[1]&&111===u[2]&&98===u[3]&&101===u[4]&&(n={version:u[5]<<8|u[6],flags0:u[7]<<8|u[8],flags1:u[9]<<8|u[10],transformCode:u[11]});break;case 65499:const d=readUint16(e,s);s+=2;const f=d+s-2;let p;for(;s<f;){const t=e[s++],i=new Uint16Array(64);if(t>>4==0)for(Q=0;Q<64;Q++){p=ti[Q];i[p]=e[s++]}else{if(t>>4!=1)throw new JpegError("DQT - invalid table spec");for(Q=0;Q<64;Q++){p=ti[Q];i[p]=readUint16(e,s);s+=2}}o[15&t]=i}break;case 65472:case 65473:case 65474:if(i)throw new JpegError("Only single frame JPEGs supported");s+=2;i={};i.extended=65473===h;i.progressive=65474===h;i.precision=e[s++];const m=readUint16(e,s);s+=2;i.scanLines=t||m;i.samplesPerLine=readUint16(e,s);s+=2;i.components=[];i.componentIds={};const y=e[s++];let w=0,D=0;for(l=0;l<y;l++){const t=e[s],a=e[s+1]>>4,r=15&e[s+1];w<a&&(w=a);D<r&&(D=r);const n=e[s+2];E=i.components.push({h:a,v:r,quantizationId:n,quantizationTable:null});i.componentIds[t]=E-1;s+=3}i.maxH=w;i.maxV=D;prepareComponents(i);break;case 65476:const b=readUint16(e,s);s+=2;for(l=2;l<b;){const t=e[s++],i=new Uint8Array(16);let a=0;for(Q=0;Q<16;Q++,s++)a+=i[Q]=e[s];const r=new Uint8Array(a);for(Q=0;Q<a;Q++,s++)r[Q]=e[s];l+=17+a;(t>>4==0?C:c)[15&t]=buildHuffmanTable(i,r)}break;case 65501:s+=2;a=readUint16(e,s);s+=2;break;case 65498:const F=1==++g&&!t;s+=2;const S=e[s++],k=[];for(l=0;l<S;l++){const t=e[s++],a=i.componentIds[t],r=i.components[a];r.index=t;const n=e[s++];r.huffmanTableDC=C[n>>4];r.huffmanTableAC=c[15&n];k.push(r)}const R=e[s++],N=e[s++],G=e[s++];try{const t=decodeScan(e,s,i,k,a,R,N,G>>4,15&G,F);s+=t}catch(t){if(t instanceof DNLMarkerError){warn(`${t.message} -- attempting to re-parse the JPEG image.`);return this.parse(e,{dnlScanLines:t.scanLines})}if(t instanceof EOIMarkerError){warn(`${t.message} -- ignoring the rest of the image data.`);break A}throw t}break;case 65500:s+=4;break;case 65535:255!==e[s]&&s--;break;default:const x=findNextFileMarker(e,s-2,s-3);if(x?.invalid){warn("JpegImage.parse - unexpected data, current marker is: "+x.invalid);s=x.offset;break}if(!x||s>=e.length-1){warn("JpegImage.parse - reached the end of the image data without finding an EOI marker (0xFFD9).");break A}throw new JpegError("JpegImage.parse - unknown marker: "+h.toString(16))}h=readUint16(e,s);s+=2}if(!i)throw new JpegError("JpegImage.parse - no frame data found.");this.width=i.samplesPerLine;this.height=i.scanLines;this.jfif=r;this.adobe=n;this.components=[];for(const e of i.components){const t=o[e.quantizationId];t&&(e.quantizationTable=t);this.components.push({index:e.index,output:buildComponentData(0,e),scaleX:e.h/i.maxH,scaleY:e.v/i.maxV,blocksPerLine:e.blocksPerLine,blocksPerColumn:e.blocksPerColumn})}this.numComponents=this.components.length}_getLinearizedBlockData(e,t,i=!1){const a=this.width/e,s=this.height/t;let r,n,g,o,c,C,h,l,Q,E,u,d=0;const f=this.components.length,p=e*t*f,m=new Uint8ClampedArray(p),y=new Uint32Array(e),w=4294967288;let D;for(h=0;h<f;h++){r=this.components[h];n=r.scaleX*a;g=r.scaleY*s;d=h;u=r.output;o=r.blocksPerLine+1<<3;if(n!==D){for(c=0;c<e;c++){l=0|c*n;y[c]=(l&w)<<3|7&l}D=n}for(C=0;C<t;C++){l=0|C*g;E=o*(l&w)|(7&l)<<3;for(c=0;c<e;c++){m[d]=u[E+y[c]];d+=f}}}let b=this._decodeTransform;i||4!==f||b||(b=new Int32Array([-256,255,-256,255,-256,255,-256,255]));if(b)for(h=0;h<p;)for(l=0,Q=0;l<f;l++,h++,Q+=2)m[h]=(m[h]*b[Q]>>8)+b[Q+1];return m}get _isColorConversionNeeded(){return this.adobe?!!this.adobe.transformCode:3===this.numComponents?0!==this._colorTransform&&(82!==this.components[0].index||71!==this.components[1].index||66!==this.components[2].index):1===this._colorTransform}_convertYccToRgb(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=3){t=e[s];i=e[s+1];a=e[s+2];e[s]=t-179.456+1.402*a;e[s+1]=t+135.459-.344*i-.714*a;e[s+2]=t-226.816+1.772*i}return e}_convertYccToRgba(e,t){for(let i=0,a=0,s=e.length;i<s;i+=3,a+=4){const s=e[i],r=e[i+1],n=e[i+2];t[a]=s-179.456+1.402*n;t[a+1]=s+135.459-.344*r-.714*n;t[a+2]=s-226.816+1.772*r;t[a+3]=255}return t}_convertYcckToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=i*(-660635669420364e-19*i+.000437130475926232*a-54080610064599e-18*t+.00048449797120281*s-.154362151871126)-122.67195406894+a*(-.000957964378445773*a+.000817076911346625*t-.00477271405408747*s+1.53380253221734)+t*(.000961250184130688*t-.00266257332283933*s+.48357088451265)+s*(-.000336197177618394*s+.484791561490776);e[r++]=107.268039397724+i*(219927104525741e-19*i-.000640992018297945*a+.000659397001245577*t+.000426105652938837*s-.176491792462875)+a*(-.000778269941513683*a+.00130872261408275*t+.000770482631801132*s-.151051492775562)+t*(.00126935368114843*t-.00265090189010898*s+.25802910206845)+s*(-.000318913117588328*s-.213742400323665);e[r++]=i*(-.000570115196973677*i-263409051004589e-19*a+.0020741088115012*t-.00288260236853442*s+.814272968359295)-20.810012546947+a*(-153496057440975e-19*a-.000132689043961446*t+.000560833691242812*s-.195152027534049)+t*(.00174418132927582*t-.00255243321439347*s+.116935020465145)+s*(-.000343531996510555*s+.24165260232407)}return e.subarray(0,r)}_convertYcckToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=a*(-660635669420364e-19*a+.000437130475926232*s-54080610064599e-18*i+.00048449797120281*r-.154362151871126)-122.67195406894+s*(-.000957964378445773*s+.000817076911346625*i-.00477271405408747*r+1.53380253221734)+i*(.000961250184130688*i-.00266257332283933*r+.48357088451265)+r*(-.000336197177618394*r+.484791561490776);e[t+1]=107.268039397724+a*(219927104525741e-19*a-.000640992018297945*s+.000659397001245577*i+.000426105652938837*r-.176491792462875)+s*(-.000778269941513683*s+.00130872261408275*i+.000770482631801132*r-.151051492775562)+i*(.00126935368114843*i-.00265090189010898*r+.25802910206845)+r*(-.000318913117588328*r-.213742400323665);e[t+2]=a*(-.000570115196973677*a-263409051004589e-19*s+.0020741088115012*i-.00288260236853442*r+.814272968359295)-20.810012546947+s*(-153496057440975e-19*s-.000132689043961446*i+.000560833691242812*r-.195152027534049)+i*(.00174418132927582*i-.00255243321439347*r+.116935020465145)+r*(-.000343531996510555*r+.24165260232407);e[t+3]=255}return e}_convertYcckToCmyk(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=4){t=e[s];i=e[s+1];a=e[s+2];e[s]=434.456-t-1.402*a;e[s+1]=119.541-t+.344*i+.714*a;e[s+2]=481.816-t-1.772*i}return e}_convertCmykToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=255+t*(-6747147073602441e-20*t+.0008379262121013727*i+.0002894718188643294*a+.003264231057537806*s-1.1185611867203937)+i*(26374107616089405e-21*i-8626949158638572e-20*a-.0002748769067499491*s-.02155688794978967)+a*(-3878099212869363e-20*a-.0003267808279485286*s+.0686742238595345)-s*(.0003361971776183937*s+.7430659151342254);e[r++]=255+t*(.00013596372813588848*t+.000924537132573585*i+.00010567359618683593*a+.0004791864687436512*s-.3109689587515875)+i*(-.00023545346108370344*i+.0002702845253534714*a+.0020200308977307156*s-.7488052167015494)+a*(6834815998235662e-20*a+.00015168452363460973*s-.09751927774728933)-s*(.0003189131175883281*s+.7364883807733168);e[r++]=255+t*(13598650411385307e-21*t+.00012423956175490851*i+.0004751985097583589*a-36729317476630422e-22*s-.05562186980264034)+i*(.00016141380598724676*i+.0009692239130725186*a+.0007782692450036253*s-.44015232367526463)+a*(5.068882914068769e-7*a+.0017778369011375071*s-.7591454649749609)-s*(.0003435319965105553*s+.7063770186160144)}return e.subarray(0,r)}_convertCmykToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=255+i*(-6747147073602441e-20*i+.0008379262121013727*a+.0002894718188643294*s+.003264231057537806*r-1.1185611867203937)+a*(26374107616089405e-21*a-8626949158638572e-20*s-.0002748769067499491*r-.02155688794978967)+s*(-3878099212869363e-20*s-.0003267808279485286*r+.0686742238595345)-r*(.0003361971776183937*r+.7430659151342254);e[t+1]=255+i*(.00013596372813588848*i+.000924537132573585*a+.00010567359618683593*s+.0004791864687436512*r-.3109689587515875)+a*(-.00023545346108370344*a+.0002702845253534714*s+.0020200308977307156*r-.7488052167015494)+s*(6834815998235662e-20*s+.00015168452363460973*r-.09751927774728933)-r*(.0003189131175883281*r+.7364883807733168);e[t+2]=255+i*(13598650411385307e-21*i+.00012423956175490851*a+.0004751985097583589*s-36729317476630422e-22*r-.05562186980264034)+a*(.00016141380598724676*a+.0009692239130725186*s+.0007782692450036253*r-.44015232367526463)+s*(5.068882914068769e-7*s+.0017778369011375071*r-.7591454649749609)-r*(.0003435319965105553*r+.7063770186160144);e[t+3]=255}return e}getData({width:e,height:t,forceRGBA:i=!1,forceRGB:a=!1,isSourcePDF:s=!1}){if(this.numComponents>4)throw new JpegError("Unsupported color mode");const r=this._getLinearizedBlockData(e,t,s);if(1===this.numComponents&&(i||a)){const e=r.length*(i?4:3),t=new Uint8ClampedArray(e);let a=0;if(i)!function grayToRGBA(e,t){if(FeatureTest.isLittleEndian)for(let i=0,a=e.length;i<a;i++)t[i]=65793*e[i]|4278190080;else for(let i=0,a=e.length;i<a;i++)t[i]=16843008*e[i]|255}(r,new Uint32Array(t.buffer));else for(const e of r){t[a++]=e;t[a++]=e;t[a++]=e}return t}if(3===this.numComponents&&this._isColorConversionNeeded){if(i){const e=new Uint8ClampedArray(r.length/3*4);return this._convertYccToRgba(r,e)}return this._convertYccToRgb(r)}if(4===this.numComponents){if(this._isColorConversionNeeded)return i?this._convertYcckToRgba(r):a?this._convertYcckToRgb(r):this._convertYcckToCmyk(r);if(i)return this._convertCmykToRgba(r);if(a)return this._convertCmykToRgb(r)}return r}}class JpegStream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}decodeImage(e){if(this.eof)return this.buffer;e||=this.bytes;for(let t=0,i=e.length-1;t<i;t++)if(255===e[t]&&216===e[t+1]){t>0&&(e=e.subarray(t));break}const t={decodeTransform:void 0,colorTransform:void 0},i=this.dict.getArray("D","Decode");if((this.forceRGBA||this.forceRGB)&&Array.isArray(i)){const e=this.dict.get("BPC","BitsPerComponent")||8,a=i.length,s=new Int32Array(a);let r=!1;const n=(1<<e)-1;for(let e=0;e<a;e+=2){s[e]=256*(i[e+1]-i[e])|0;s[e+1]=i[e]*n|0;256===s[e]&&0===s[e+1]||(r=!0)}r&&(t.decodeTransform=s)}if(this.params instanceof Dict){const e=this.params.get("ColorTransform");Number.isInteger(e)&&(t.colorTransform=e)}const a=new JpegImage(t);a.parse(e);const s=a.getData({width:this.drawWidth,height:this.drawHeight,forceRGBA:this.forceRGBA,forceRGB:this.forceRGB,isSourcePDF:!0});this.buffer=s;this.bufferLength=s.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}var ci,Ci=(ci="undefined"!=typeof document?document.currentScript?.src:void 0,function(e={}){var t,i,a=e;new Promise(((e,a)=>{t=e;i=a}));a.decode=function(e,{numComponents:t=4,isIndexedColormap:i=!1,smaskInData:s=!1}){const r=e.length,n=a._malloc(r);a.HEAPU8.set(e,n);const g=a._jp2_decode(n,r,t>0?t:0,!!i,!!s);a._free(n);if(g){const{errorMessages:e}=a;if(e){delete a.errorMessages;return e}return"Unknown error"}const{imageData:o}=a;a.imageData=null;return o};var s,r=Object.assign({},a),n="./this.program",g="";"undefined"!=typeof document&&document.currentScript&&(g=document.currentScript.src);ci&&(g=ci);g=g.startsWith("blob:")?"":g.substr(0,g.replace(/[?#].*/,"").lastIndexOf("/")+1);var o,c,C,h,l,Q=a.print||console.log.bind(console),E=a.printErr||console.error.bind(console);Object.assign(a,r);r=null;a.arguments&&a.arguments;a.thisProgram&&(n=a.thisProgram);a.quit&&a.quit;a.wasmBinary&&(o=a.wasmBinary);function tryParseAsDataURI(e){if(isDataURI(e))return function intArrayFromBase64(e){for(var t=atob(e),i=new Uint8Array(t.length),a=0;a<t.length;++a)i[a]=t.charCodeAt(a);return i}(e.slice(D.length))}function updateMemoryViews(){var e=c.buffer;a.HEAP8=C=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAPU8=h=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAP32=new Int32Array(e);a.HEAPU32=l=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}var u,d=[],f=[],p=[],m=0,y=null,w=null,D="data:application/octet-stream;base64,",isDataURI=e=>e.startsWith(D);function instantiateSync(e,t){var i,a=function getBinarySync(e){if(e==u&&o)return new Uint8Array(o);var t=tryParseAsDataURI(e);if(t)return t;if(s)return s(e);throw'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)'}(e);i=new WebAssembly.Module(a);return[new WebAssembly.Instance(i,t),i]}var callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(a)};a.noExitRuntime;var b,growMemory=e=>{var t=(e-c.buffer.byteLength+65535)/65536;try{c.grow(t);updateMemoryViews();return 1}catch(e){}},F={},getEnvStrings=()=>{if(!getEnvStrings.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:n||"./this.program"};for(var t in F)void 0===F[t]?delete e[t]:e[t]=F[t];var i=[];for(var t in e)i.push(`${t}=${e[t]}`);getEnvStrings.strings=i}return getEnvStrings.strings},S=[null,[],[]],k="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,UTF8ArrayToString=(e,t,i)=>{for(var a=t+i,s=t;e[s]&&!(s>=a);)++s;if(s-t>16&&e.buffer&&k)return k.decode(e.subarray(t,s));for(var r="";t<s;){var n=e[t++];if(128&n){var g=63&e[t++];if(192!=(224&n)){var o=63&e[t++];if((n=224==(240&n)?(15&n)<<12|g<<6|o:(7&n)<<18|g<<12|o<<6|63&e[t++])<65536)r+=String.fromCharCode(n);else{var c=n-65536;r+=String.fromCharCode(55296|c>>10,56320|1023&c)}}else r+=String.fromCharCode((31&n)<<6|g)}else r+=String.fromCharCode(n)}return r},printChar=(e,t)=>{var i=S[e];if(0===t||10===t){(1===e?Q:E)(UTF8ArrayToString(i,0));i.length=0}else i.push(t)},UTF8ToString=(e,t)=>e?UTF8ArrayToString(h,e,t):"",R={c:(e,t,i)=>h.copyWithin(e,t,t+i),g:function _copy_pixels_1(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(t),s=a.HEAP32.subarray(e,e+t);i.set(s)},f:function _copy_pixels_3(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(3*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[3*e]=n[e];r[3*e+1]=g[e];r[3*e+2]=o[e]}},e:function _copy_pixels_4(e,t,i,s,r){e>>=2;t>>=2;i>>=2;s>>=2;const n=a.imageData=new Uint8ClampedArray(4*r),g=a.HEAP32.subarray(e,e+r),o=a.HEAP32.subarray(t,t+r),c=a.HEAP32.subarray(i,i+r),C=a.HEAP32.subarray(s,s+r);for(let e=0;e<r;e++){n[4*e]=g[e];n[4*e+1]=o[e];n[4*e+2]=c[e];n[4*e+3]=C[e]}},k:e=>{var t=h.length,i=2147483648;if((e>>>=0)>i)return!1;for(var a,s,r=1;r<=4;r*=2){var n=t*(1+.2/r);n=Math.min(n,e+100663296);var g=Math.min(i,(a=Math.max(e,n))+((s=65536)-a%s)%s);if(growMemory(g))return!0}return!1},l:(e,t)=>{var i=0;getEnvStrings().forEach(((a,s)=>{var r=t+i;l[e+4*s>>2]=r;((e,t)=>{for(var i=0;i<e.length;++i)C[t++]=e.charCodeAt(i);C[t]=0})(a,r);i+=a.length+1}));return 0},m:(e,t)=>{var i=getEnvStrings();l[e>>2]=i.length;var a=0;i.forEach((e=>a+=e.length+1));l[t>>2]=a;return 0},n:e=>52,j:function _fd_seek(e,t,i,a,s){return 70},b:(e,t,i,a)=>{for(var s=0,r=0;r<i;r++){var n=l[t>>2],g=l[t+4>>2];t+=8;for(var o=0;o<g;o++)printChar(e,h[n+o]);s+=g}l[a>>2]=s;return 0},o:function _gray_to_rgba(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(4*t),s=a.HEAP32.subarray(e,e+t);for(let e=0;e<t;e++){i[4*e]=i[4*e+1]=i[4*e+2]=s[e];i[4*e+3]=255}},i:function _graya_to_rgba(e,t,i){e>>=2;t>>=2;const s=a.imageData=new Uint8ClampedArray(4*i),r=a.HEAP32.subarray(e,e+i),n=a.HEAP32.subarray(t,t+i);for(let e=0;e<i;e++){s[4*e]=s[4*e+1]=s[4*e+2]=r[e];s[4*e+3]=n[e]}},d:function _jsPrintWarning(e){const t=UTF8ToString(e);(a.warn||console.warn)(`OpenJPEG: ${t}`)},h:function _rgb_to_rgba(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(4*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[4*e]=n[e];r[4*e+1]=g[e];r[4*e+2]=o[e];r[4*e+3]=255}},a:function _storeErrorMessage(e){const t=UTF8ToString(e);a.errorMessages?a.errorMessages+="\n"+t:a.errorMessages=t}},N=function createWasm(){var e=function getWasmImports(){return{a:R}}();function receiveInstance(e,t){N=e.exports;c=N.p;updateMemoryViews();!function addOnInit(e){f.unshift(e)}(N.q);!function removeRunDependency(e){m--;a.monitorRunDependencies?.(m);if(0==m){if(null!==y){clearInterval(y);y=null}if(w){var t=w;w=null;t()}}}();return N}!function addRunDependency(e){m++;a.monitorRunDependencies?.(m)}();if(a.instantiateWasm)try{return a.instantiateWasm(e,receiveInstance)}catch(e){E(`Module.instantiateWasm callback failed with error: ${e}`);i(e)}u||(u="data:application/octet-stream;base64,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A0kNAAsLAkAgA0F8cSIEQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgACADQQRrIgRLBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAswAQF/AkAgAEUNACABRQ0AQQggACABbCIBECUiAARAIABBACABEBUaCyAAIQILIAILEQAgAEUEQEEADwtBCCAAECUL8gICAn8BfgJAIAJFDQAgACABOgAAIAAgAmoiA0EBayABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBA2sgAToAACADQQJrIAE6AAAgAkEHSQ0AIAAgAToAAyADQQRrIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBBGsgATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQQhrIAE2AgAgAkEMayABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkEQayABNgIAIAJBFGsgATYCACACQRhrIAE2AgAgAkEcayABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa1CgYCAgBB+IQUgAyAEaiEBA0AgASAFNwMYIAEgBTcDECABIAU3AwggASAFNwMAIAFBIGohASACQSBrIgJBH0sNAAsLIAALJwEBfyMAQRBrIgMkACADIAI2AgwgACABIAJBAEEAEGsgA0EQaiQAC+gFAQl/IAFFBEBBAA8LAn8gAEUE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ACAEIE5BCmpBAiAFEBpBAkcEQCAFQQFBAiAAKAK4ARtBlhJBABAPIAAoArgBRSFnDAELIE5BCmogTkEMakECEBEgTigCDCIBQZD/A0YNACABQdn/A0YEQCAAQYACNgIIIABBADYCzAEMAQsgBCkDCCKGAVAEfkIABSCGASAEKQM4fQtQBEAgAEHAADYCCCAFQQJBrD9BABAPDAELQQAhZyAFQQFB7D5BABAPCyBOQRBqJAAgZwsLACAABEAgABAQCwu0AQEBfyAAKAIMRQRAIAIgACgCJCABEQMADwsCQEEIEBQiA0UNACADIAI2AgQgAyABNgIAQQgQFCIBRQRAIAMQEA8LIAEgAzYCACAAIAAoAgRB5ABsIgI2AigDQCAAKAIYIAJKDQALIAEgACgCFDYCBCAAIAE2AhQgACAAKAIYQQFqNgIYIAAoAhwiAUUNACABKAIAQQA2AgggACABKAIENgIcIAAgACgCIEEBazYCICABEBALC/oCAQR/AkAgAEUNACAAKAKsKCIBBEAgACgCqCgiAgRAQQAhAQNAIAAoAqwoIAFBA3RqKAIAIgMEQCADEBAgACgCqCghAgsgAUEBaiIBIAJJDQALIAAoAqwoIQELIABBADYCqCggARAQIABBADYCrCgLIAAoArQoIgEEQCABEBAgAEEANgK0KAsgACgC0CsiAQRAIAEQECAAQQA2AtArCyAAKALsKyIBBEAgARAQIABBADYC7CsLIAAoAugrIgEEQCABEBAgAEEANgLoKwsgACgC/CsiAQRAIAEQECAAQQA2AoQsIABCADcC/CsLIAAoAvArIgEEQCAAKAL0KyIDBH9BACECA0AgASgCDCIEBEAgBBAQIAFBADYCDCAAKAL0KyEDCyABQRRqIQEgAkEBaiICIANJDQALIAAoAvArBSABCxAQIABBADYC8CsLIAAoAuQrIgEEQCABEBAgAEEANgLkKwsgACgC3CsiAUUNACABEBAgAEIANwLcKwsLyAcCEX8BfiAAKAIQIghBIE8EQCAAKQMIpw8LAkAgACgCFCIDQQROBEAgACgCACICQQNrKAIAIQEgACADQQRrIgM2AhQgACACQQRrNgIADAELIANBAEwEQAwBCyADQQFxIQ0gACgCACECAkAgA0EBRgRAQRghBAwBCyADQf7///8HcSEJQRghBANAIAAgAkEB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IABBADYCiAEgACgCZBAQIABBADYCZCAALQC8AUECcUUEQCAAKAKoARAQCyAAQdAAakEAQfAAEBUaIAAoAsABEDIgAEEANgLAASAAKALEARAyIABBADYCwAEgACgCyAEiAQRAIAEoAhwiAgRAIAIQECABQQA2AhwLIAEoAigiAgRAIAEoAiQEQANAIAIgBUEobCIDaigCJCIEBEAgBBAQIAEoAigiAiADakEANgIkCyACIANqKAIQIgQEQCAEEBAgASgCKCICIANqQQA2AhALIAIgA2ooAhgiBARAIAQQECABKAIoIgIgA2pBADYCGAsgBUEBaiIFIAEoAiRJDQALCyACEBAgAUEANgIoCyABEBALIABBADYCyAEgACgCSBAhIABBADYCSCAAKAJMECEgAEEANgJMIAAoAtQBIgMEQAJAIAMoAghFDQAgAygCDARAIANBADYCKANAIAMoAhhBAEoNAAsLIANBATYCECADKAIAEBAgAygCHCICRQ0AA0AgAigCBCEBIAIQECADIAE2AhwgASICDQALCyADKAIkIgIEQCACKAIEIgVBAEoEQEEAIQEDQCACKAIAIAFBDGxqIgQoAggiBgRAIAQoAgQgBhECACACKAIEIQULIAFBAWoiASAFSA0ACwsgAigCABAQIAIQEAsgAxAQCyAAQQA2AtQBIAAQEAsL5gMCCH8EfiAAKAIUKAIAKAIUIAFBzABsaiIJKAIMIgggACgCGCgCGCABQTRsaiIKNQIEIhBCAX0iEiAANQI8fCAQgKciCyAIIAtJGyEMIAkoAggiCCAKNQIAIhFCAX0iEyAANQI4fCARgKciCiAIIApJGyEKIAkoAgQiCCASIAA1AjR8IBCApyILIAggC0sbIQsgCSgCACIIIBMgADUCMHwgEYCnIg0gCCANSxshDUEAIQggACgCICgC0CsgAUG4CGxqKAIUIQ4CQCAJKAIUQQAgAmtBfyACG2oiAkUEQCAKIQAgDSEIIAshAQwBCyADQQFxIAJBAWsiD3QiCSANSQRAIA0gCWutQn8gAq0iEIZCf4V8IBCIpyEIC0EAIQBBACEBIANBAXYgD3QiAyALSQRAIAsgA2utQn8gAq0iEIZCf4V8IBCIpyEBCyAJIApJBEAgCiAJa61CfyACrSIQhkJ/hXwgEIinIQALIAMgDE8EQEEAIQwMAQsgDCADa61CfyACrSIQhkJ/hXwgEIinIQwLQX8gAEECQQMgDkEBRhsiAmoiAyAAIANLGyAES0F/IAIgDGoiACAAIAxJGyAFS3EgCCACayIAQQAgACAITRsgBklxIAEgAmsiAEEAIAAgAU0bIAdJcQuiAQEGfyAABEAgACgCBCICBEAgAhAQIABBADYCBAsgAQRAIAAhAgNAIAIoAsgBIgMEQEEAIQUgAigCxAEiBAR/A0AgAygCDCIGBEAgBhAQIANBADYCDCACKALEASEECyADQRBqIQMgBUEBaiIFIARJDQALIAIoAsgBBSADCxAQIAJBADYCyAELIAJB8AFqIQIgB0EBaiIHIAFHDQALCyAAEBALC9UZAhN/A3sgACgCACIKIAAoAgwiDUEFdCIFaiEGIAogBWshFiAAKAIQIQUgACgCHCELIAAoAhQhCSAAKAIIIQ4CQAJAAkACQCADQQhJDQAgAUEPcQ0AIAZBD3FFDQELIAUgCU8NAgJAAkAgA0EBaw4CAAEDCwJAIAkgBWsiCEEYSQ0AIAEgBUECdGohByANQQV0IgQgCiAFQQZ0amogASAJQQJ0akkEQCAHIAogCUEGdGogBGpBPGtJDQELIAX9Ef0MAAAAAAEAAAACAAAAAwAAAP2uASEYIAUgCEF8cSIPaiEFQQAhBANAIAYgGEEE/asBIhf9GwBBAnRqIAcgBEECdGr9AAIAIhn9HwA4AgAgBiAX/RsBQQJ0aiAZ/R8BOAIAIAYgF/0bAkECdGogGf0fAjgCACAGIBf9GwNBAnRqIBn9HwM4AgAgGP0MBAAAAAQAAAAEAAAABAAAAP2uASEYIARBBGoiBCAPRw0ACyAIIA9GDQQLIAUhBCAJIAVrQQNxIgcEQEEAIQgDQCAGIARBBnRqIAEgBEECdGoqAgA4AgAgBEEBaiEEIAhBAWoiCCAHRw0ACwsgBSAJa0F8Sw0DA0AgBiAEQQZ0aiABIARBAnRqKgIAOAIAIAYgBEEBaiIFQQZ0aiABIAVBAnRqKgIAOAIAIAYgBEECaiIFQQZ0aiABIAVBAnRqKgIAOAIAIAYgBEEDaiIFQQZ0aiABIAVBAnRqKgIAOAIAIARBBGoiBCAJRw0ACwwDCyABIAJBAnRqIQgCQCAJIAVrIg9BPEkEQCAFIQQMAQsgCiAFQQZ0IA1BBXRqaiIEIAkgBUF/c2oiB0EGdCIQaiAESQRAIAUhBAwBCyAEQQRqIgQgEGogBEkEQCAFIQQMAQsgB0H///8fSwRAIAUhBAwBCyANQQV0IgQgCiAFQQZ0amoiByABIAIgCWpBAnRqSSAKIAlBBnRqIARqQThrIgQgASACIAVqQQJ0aktxBEAgBSEEDAELIAcgASAJQQJ0akkgASAFQQJ0aiAESXEEQCAFIQQMAQsgBf0R/QwAAAAAAQAAAAIAAAADAAAA/a4BIRggBSAPQXxxIhBqIQRBACEHA0AgBiAYQQT9qwEiF/0bAEECdGoiESABIAUgB2pBAnQiDGr9AAIAIhn9HwA4AgAgBiAX/RsBQQJ0aiITIBn9HwE4AgAgBiAX/RsCQQJ0aiIUIBn9HwI4AgAgBiAX/RsDQQJ0aiIVIBn9HwM4AgAgESAIIAxq/QACACIX/R8AOAIEIBMgF/0fATgCBCAUIBf9HwI4AgQgFSAX/R8DOAIEIBj9DAQAAAAEAAAABAAAAAQAAAD9rgEhGCAHQQRqIgcgEEcNAAsgDyAQRg0DCyAEQQFqIQUgCSAEa0EBcQRAIAYgBEEGdGoiByABIARBAnQiBGoqAgA4AgAgByAEIAhqKgIAOAIEIAUhBAsgBSAJRg0CA0AgBiAEQQZ0aiIFIAEgBEECdCIHaioCADgCACAFIAcgCGoqAgA4AgQgBiAEQQFqIgVBBnRqIgcgASAFQQJ0IgVqKgIAOAIAIAcgBSAIaioCADgCBCAEQQJqIgQgCUcNAAsMAgsgBSAJTw0BIAEgAkECdGohCANAIAYgBUEGdGoiBCABIAVBAnRqKgIAOAIAIAQgASACIAVqIgdBAnRqKgIAOAIEIAQgASACIAdqIgdBAnRqKgIAOAIIIAQgASACIAdqIgdBAnRqKgIAOAIMIAQgASACIAdqIgdBAnRqKgIAOAIQIAQgASACIAdqIgdBAnRqKgIAOAIUIAQgASACIAdqQQJ0IgdqKgIAOAIYIAQgByAIaioCADgCHCAFQQFqIgUgCUcNAAsMAQsgASACQQJ0aiEIIANBA0YhByADQQRGIQ8gA0EFRiEQIANBB0YhEQNAIAYgBUEGdGoiBCABIAVBAnRqKgIAOAIAIAQgASACIAVqIgxBAnRqKgIAOAIEIAQgASACIAxqIgxBAnRqKgIAOAIIAkAgBw0AIAQgASACIAxqIgxBAnRqKgIAOAIMIA8NACAEIAEgAiAMaiIMQQJ0aioCADgCECAQDQAgBCABIAIgDGoiDEECdGoqAgA4AhQgA0EGRg0AIAQgASACIAxqQQJ0IgxqKgIAOAIYIBENACAEIAggDGoqAgA4AhwLIAVBAWoiBSAJRw0ACwsgFkEgaiEGIAEgDkECdGohBCAAKAIYIQUCQAJAAkAgA0EISQ0AIARBD3ENACAGQQ9xRQ0BCyAFIAtPDQECQAJAAkAgA0EBaw4CAAECCwJAIAsgBWsiAEEcSQ0AIAogBUEGdEEgciANQQV0IgJraiABIAsgDmpBAnRqSQRAIAEgBSAOakECdGogC0EGdCACayAKakEca0kNAQsgBCAFQQJ0aiEDIAX9Ef0MAAAAAAEAAAACAAAAAwAAAP2uASEYIAUgAEF8cSIBaiEFQQAhAgNAIAYgGEEE/asBIhf9GwBBAnRqIAMgAkECdGr9AAIAIhn9HwA4AgAgBiAX/RsBQQJ0aiAZ/R8BOAIAIAYgF/0bAkECdGogGf0fAjgCACAGIBf9GwNBAnRqIBn9HwM4AgAgGP0MBAAAAAQAAAAEAAAABAAAAP2uASEYIAJBBGoiAiABRw0ACyAAIAFGDQQLIAUhAiALIAVrQQNxIgAEQEEAIQEDQCAGIAJBBnRqIAQgAkECdGoqAgA4AgAgAkEBaiECIAFBAWoiASAARw0ACwsgBSALa0F8Sw0DA0AgBiACQQZ0aiAEIAJBAnRqKgIAOAIAIAYgAkEBaiIAQQZ0aiAEIABBAnRqKgIAOAIAIAYgAkECaiIAQQZ0aiAEIABBAnRqKgIAOAIAIAYgAkEDaiIAQQZ0aiAEIABBAnRqKgIAOAIAIAJBBGoiAiALRw0ACwwDCyAEIAJBAnRqIQMCQCALIAVrIgBBxABJBEAgBSECDAELIAogBUEGdCIJQSByIA1BBXQiCGtqIgcgCyAFQX9zaiIPQQZ0IhBqIAdJBEAgBSECDAELIAogCUEkciAIa2oiCSAQaiAJSQRAIAUhAgwBCyAPQf///x9LBEAgBSECDAELIAogBUEGdEEgciANQQV0IglraiINIAEgCyAOaiIIIAJqQQJ0akkgC0EGdCAJayAKakEYayIJIAEgDkECdGogBUECdGoiCiACQQJ0aktxBEAgBSECDAELIA0gASAIQQJ0akkgCSAKS3EEQCAFIQIMAQsgBf0R/QwAAAAAAQAAAAIAAAADAAAA/a4BIRggBSAAQXxxIglqIQJBACEBA0AgBiAYQQT9qwEiF/0bAEECdGoiCiAEIAEgBWpBAnQiDWr9AAIAIhn9HwA4AgAgBiAX/RsBQQJ0aiIOIBn9HwE4AgAgBiAX/RsCQQJ0aiIIIBn9HwI4AgAgBiAX/RsDQQJ0aiIHIBn9HwM4AgAgCiADIA1q/QACACIX/R8AOAIEIA4gF/0fATgCBCAIIBf9HwI4AgQgByAX/R8DOAIEIBj9DAQAAAAEAAAABAAAAAQAAAD9rgEhGCABQQRqIgEgCUcNAAsgACAJRg0DCyACQQFqIQAgCyACa0EBcQRAIAYgAkEGdGoiASAEIAJBAnQiAmoqAgA4AgAgASACIANqKgIAOAIEIAAhAgsgACALRg0CA0AgBiACQQZ0aiIAIAQgAkECdCIBaioCADgCACAAIAEgA2oqAgA4AgQgBiACQQFqIgBBBnRqIgEgBCAAQQJ0IgBqKgIAOAIAIAEgACADaioCADgCBCACQQJqIgIgC0cNAAsMAgsgBCACQQJ0aiEBIANBA0YhCSADQQRGIQogA0EFRiENIANBB0YhDgNAIAYgBUEGdGoiACAEIAVBAnRqKgIAOAIAIAAgBCACIAVqIghBAnRqKgIAOAIEIAAgBCACIAhqIghBAnRqKgIAOAIIAkAgCQ0AIAAgBCACIAhqIghBAnRqKgIAOAIMIAoNACAAIAQgAiAIaiIIQQJ0aioCADgCECANDQAgACAEIAIgCGoiCEECdGoqAgA4AhQgA0EGRg0AIAAgBCACIAhqQQJ0IghqKgIAOAIYIA4NACAAIAEgCGoqAgA4AhwLIAVBAWoiBSALRw0ACwwBCyAFIAtPDQAgBCACQQJ0aiEBA0AgBiAFQQZ0aiIAIAQgBUECdGoqAgA4AgAgACAEIAIgBWoiA0ECdGoqAgA4AgQgACAEIAIgA2oiA0ECdGoqAgA4AgggACAEIAIgA2oiA0ECdGoqAgA4AgwgACAEIAIgA2oiA0ECdGoqAgA4AhAgACAEIAIgA2oiA0ECdGoqAgA4AhQgACAEIAIgA2pBAnQiA2oqAgA4AhggACABIANqKgIAOAIcIAVBAWoiBSALRw0ACwsLmwMBBH8gASAAQQRqIgRqQQFrQQAgAWtxIgUgAmogACAAKAIAIgFqQQRrTQR/IAAoAgQiAyAAKAIIIgY2AgggBiADNgIEIAQgBUcEQCAAIABBBGsoAgBBfnFrIgMgBSAEayIEIAMoAgBqIgU2AgAgAyAFQXxxakEEayAFNgIAIAAgBGoiACABIARrIgE2AgALAn8gASACQRhqTwRAIAAgAmpBCGoiAyABIAJrQQhrIgE2AgAgAyABQXxxakEEayABQQFyNgIAIAMCfyADKAIAQQhrIgFB/wBNBEAgAUEDdkEBawwBCyABZyEEIAFBHSAEa3ZBBHMgBEECdGtB7gBqIAFB/x9NDQAaQT8gAUEeIARrdkECcyAEQQF0a0HHAGoiASABQT9PGwsiAUEEdCIEQaDHAWo2AgQgAyAEQajHAWoiBCgCADYCCCAEIAM2AgAgAygCCCADNgIEQajPAUGozwEpAwBCASABrYaENwMAIAAgAkEIaiIBNgIAIAAgAUF8cWoMAQsgACABagtBBGsgATYCACAAQQRqBUEACwvCAQEDfwJAIAEgAigCECIDBH8gAwUgAhA+DQEgAigCEAsgAigCFCIEa0sEQCACIAAgASACKAIkEQAADwsCQAJAIAIoAlBBAEgNACABRQ0AIAEhAwNAIAAgA2oiBUEBay0AAEEKRwRAIANBAWsiAw0BDAILCyACIAAgAyACKAIkEQAAIgQgA0kNAiABIANrIQEgAigCFCEEDAELIAAhBUEAIQMLIAQgBSABEBIaIAIgAigCFCABajYCFCABIANqIQQLIAQLWQEBfyAAIAAoAkgiAUEBayABcjYCSCAAKAIAIgFBCHEEQCAAIAFBIHI2AgBBfw8LIABCADcCBCAAIAAoAiwiATYCHCAAIAE2AhQgACABIAAoAjBqNgIQQQALzAIBBH8gASAA/QACAP0LAgAgASgCGCICBEAgASgCECIDBH9BACECA0AgASgCGCACQTRsaigCLCIEBEAgBBAQIAEoAhAhAwsgAkEBaiICIANJDQALIAEoAhgFIAILEBAgAUEANgIYCyABIAAoAhAiAjYCECABIAJBNGwQFCICNgIYIAIEQCABKAIQBEBBACEDA0AgAiADQTRsIgVqIgIgACgCGCAFaiIE/QACAP0LAgAgAiAEKAIwNgIwIAIgBP0AAiD9CwIgIAIgBP0AAhD9CwIQIAEoAhgiAiAFakEANgIsIANBAWoiAyABKAIQSQ0ACwsgASAAKAIUNgIUIAEgACgCICICNgIgIAIEQCABIAIQFCICNgIcIAJFBEAgAUIANwIcDwsgAiAAKAIcIAAoAiAQEhoPCyABQQA2AhwPCyABQQA2AhAgAUEANgIYCwQAQQELxgEBA38DQCAAQQR0IgFBpMcBaiABQaDHAWoiAjYCACABQajHAWogAjYCACAAQQFqIgBBwABHDQALQTAQbRojAEEQayIAJAACQCAAQQxqIABBCGoQDA0AQbDPAUEIIAAoAgxBAnRBBGoQJSIBNgIAIAFFDQBBCCAAKAIIECUiAQRAQbDPASgCACICIAAoAgxBAnRqQQA2AgAgAiABEAtFDQELQbDPAUEANgIACyAAQRBqJABBzM8BQSo2AgBBlNABQdjQATYCAAuQBgIFfwN7IwBBEGsiBiQAAn8gACgCCEEQRgRAIAAoApwBIAAoAswBQYwsbGoMAQsgACgCDAshAAJAIAMoAgAiBUUEQEEAIQIgBEEBQcATQQAQDwwBCyAAKALQKyEJIAMgBUEBazYCACACIAZBDGpBARARIAkgAUG4CGxqIgcgBigCDCIAQQV2NgKkBiAHIABBH3EiATYCGCACQQFqIQAgAwJ/An8CQAJ/AkACQCABDgIAAwELIAMoAgAMAQsgAygCAEEBdgsiBUHiAE8EfyAGQuGAgICQDDcCBCAGIAU2AgAgBEECQcX4ACAGEA8gBygCGAUgAQsEQCAFIgENAUEADAILIAUEQCAHQRxqIQFBACECA0AgACAGQQxqQQEQESACQeAATQRAIAYoAgwhBCABIAJBA3RqIghBADYCBCAIIARBA3Y2AgALIABBAWohACACQQFqIgIgBUcNAAsLQQAhAiADKAIAIgAgBUkNAyAAIAVrDAILIAdBHGohBEEAIQIDQCAAIAZBDGpBAhARIAJB4ABNBEAgBCACQQN0aiIFIAYoAgwiCEH/D3E2AgQgBSAIQQt2NgIACyAAQQJqIQAgAkEBaiICIAFHDQALIAFBAXQLIQBBACECIAMoAgAiASAASQ0BIAEgAGsLNgIAQQEhAiAHKAIYQQFHDQAgB0EcaiEEIAf9CQIcIQwgBygCICED/QwBAAAAAgAAAAMAAAAEAAAAIQtBACEBA0AgBCABQQN0aiIAQRhqIAwgC/0M//////////////////////2uASIK/RsAQQNu/REgCv0bAUEDbv0cASAK/RsCQQNu/RwCIAr9GwNBA279HAP9sQH9DAAAAAAAAAAAAAAAAAAAAAD9uAEiCv1aAgACIABBEGogCv1aAgABIABBCGogCv1aAgAAIAQgAUEEaiIBQQN0aiIFIAr9WgIAAyAAIAM2AhwgACADNgIUIAAgAzYCDCAFIAM2AgQgC/0MBAAAAAQAAAAEAAAABAAAAP2uASELIAFB4ABHDQALCyAGQRBqJAAgAgufBgEGfyMAQSBrIgYkAAJ/IAAoAghBEEYEQCAAKAKcASAAKALMAUGMLGxqDAELIAAoAgwLIQUCQCADKAIAQQRNBEBBACEAIARBAUGdE0EAEA8MAQsgAiAFKALQKyABQbgIbGoiBSIJQQRqQQEQESAFIAUoAgRBAWoiBzYCBCAHQSJPBEAgBkEhNgIEIAYgBzYCACAEQQFB+TkgBhAPQQAhAAwBCyAHIAAoAqABIghNBEAgBiAHNgIYIAYgCDYCFCAGIAE2AhAgBEEBQbT7ACAGQRBqEA8gACAAKAIIQYCAAnI2AghBACEADAELIAJBAWogBUEIakEBEBEgBSAFKAIIQQJqNgIIIAJBAmogBUEMakEBEBEgBSAFKAIMQQJqIgA2AgwCQAJAIAUoAggiAUEKSw0AIABBCksNACAAIAFqQQ1JDQELQQAhACAEQQFBwylBABAPDAELIAJBA2ogBUEQakEBEBEgBS0AEEGAAXEEQEEAIQAgBEEBQYsyQQAQDwwBCyACQQRqIAVBFGpBARARIAUoAhRBAk8EQEEAIQAgBEEBQcoxQQAQDwwBCyADIAMoAgBBBWsiBzYCAEEBIQAgBSgCBCEBIAUtAABBAXFFBEAgAUUNASAFQbAHaiEBIAVBrAZqIQJBACEFA0AgAiAFQQJ0IgBqQQ82AgAgACABakEPNgIAQQEhACAFQQFqIgUgCSgCBEkNAAsMAQsgASAHTQRAAkAgAUUEQEEAIQEMAQsgAkEFaiAGQRxqQQEQESAFIAYoAhwiAEEEdjYCsAcgBSAAQQ9xNgKsBiAFKAIEIgFBAk8EQCAFQbAHaiEHIAVBrAZqIQggAkEGaiEAQQEhBQNAIAAgBkEcakEBEBECQCAGKAIcIgFBEE8EQCABQQ9xIgINAQtBACEAIARBAUHwLUEAEA8MBQsgCCAFQQJ0IgpqIAI2AgAgByAKaiABQQR2NgIAIABBAWohACAFQQFqIgUgCSgCBCIBSQ0ACwsgAygCACEHCyADIAcgAWs2AgBBASEADAELQQAhACAEQQFBnRNBABAPCyAGQSBqJAAgAAtSACABIAAtAAA6AAcgASAALQABOgAGIAEgAC0AAjoABSABIAAtAAM6AAQgASAALQAEOgADIAEgAC0ABToAAiABIAAtAAY6AAEgASAALQAHOgAAC5IBAQR/IAAgATYCoAECQCAAKAJIIgNFDQAgAygCGCIGRQ0AIAAoAgwiBEUNACAEKALQK0UNACADKAIQIgRFBEBBAQ8LQQAhAwNAIAEgACgCDCgC0CsgA0G4CGxqKAIETwRAIAJBAUGixQBBABAPQQAPCyAGIANBNGxqIAE2AihBASEFIANBAWoiAyAERw0ACwsgBQusBwIJfwh+IwBBEGsiCiQAAkAgAkUEQCADQQFB+tUAQQAQDwwBCyACKAIQIgsgACgCSCIGKAIQSQRAIANBAUG1zgBBABAPDAELIAQgACgCaCIFIAAoAmxsIgdPBEAgCiAENgIAIAogB0EBazYCBCADQQFB9/oAIAoQD0EAIQUMAQsgAiAAKAJUIAQgBSAEIAVuIgdsayIIIAAoAlxsaiIFNgIAIAIgBSAGKAIAIgYgBSAGSxsiBjYCACACIAAoAlQgACgCXCAIQQFqbGoiBTYCCCACIAUgACgCSCgCCCIIIAUgCEkbIgg2AgggAiAAKAJYIAAoAmAgB2xqIgU2AgQgAiAFIAAoAkgoAgQiCSAFIAlLGyIJNgIEIAIgACgCWCAAKAJgIAdBAWpsaiIFNgIMIAIgBSAAKAJIKAIMIgcgBSAHSRsiBTYCDCAAKAJIIgwoAhAiBwRAIAWsQgF9IREgCKxCAX0hEiAJrUIBfSETIAatQgF9IRQgDCgCGCEIIAIoAhghBUEAIQYDQCAFIAggBkE0bGooAigiCTYCKCAFIBQgBSgCACIMrSIOfCAOgCIVPgIQIAUgEyAFKAIEIg2tIg58IA6AIhA+AhQgBUJ/IAmtIg6GIg8gEMR9IA6HpyAPIBEgDawiEHwgEH/EfSAOh6drNgIMIAUgDyAVxH0gDoenIA8gEiAMrCIPfCAPf8R9IA6Hp2s2AgggBUE0aiEFIAZBAWoiBiAHRw0ACwsgByALSQRAIAIoAhghBQNAIAUgB0E0bCIGaigCLBAQIAIoAhgiBSAGakEANgIsIAdBAWoiByACKAIQSQ0ACyACIAAoAkgoAhA2AhALIAAoAkwiBQRAIAUQIQsgAEEBQSQQEyIHNgJMQQAhBSAHRQ0AIAIgBxA/IAAgBDYCLCAAKALAAUEXIAMQJEUNACAAKALAASIEKAIAIQYgBCgCCCEHAkAgBgRAQQEhBSAGQQFxIQsgBkEBRgR/QQAFIAZBfnEhCEEAIQYDQAJ/QQAgBUUNABpBACAAIAEgAyAHKAIAEQAARQ0AGiAAIAEgAyAHKAIEEQAAQQBHCyEFIAdBCGohByAGQQJqIgYgCEcNAAsgBUEBcwshBgJAAkAgCwRAIAYNASAAIAEgAyAHKAIAEQAAQQBHIQULIARBADYCACAFQQFxRQ0BDAMLIARBADYCAAsgACgCSBAhQQAhBSAAQQA2AkgMAgsgBEEANgIACyAAIAIQRyEFCyAKQRBqJAAgBQvyAwEFfwJAAkAgACgCPCICRQRAIAEoAhANAUEBDwsgAkE0bBAUIgVFDQEgASgCEARAIAEoAhghAgNAIAIgA0E0bCIEaigCLBAQIAEoAhgiAiAEakEANgIsIANBAWoiAyABKAIQIgRJDQALCyABIAAoAjwEfyAAKAJMKAIYIQNBACECA0AgBSACQTRsaiIEIAMgACgCQCACQQJ0aigCAEE0bCIGaiID/QACAP0LAgAgBCADKAIwNgIwIAQgA/0AAiD9CwIgIAQgA/0AAhD9CwIQIAQgACgCTCgCGCIDIAZqIgYoAiQ2AiQgBCAGKAIsNgIsIAZBADYCLCACQQFqIgIgACgCPCIGSQ0ACyABKAIQBSAECwR/IAAoAkwoAhghAkEAIQMDQCACIANBNGwiBGooAiwQECAAKAJMKAIYIgIgBGpBADYCLCADQQFqIgMgASgCEEkNAAsgACgCPAUgBgs2AhAgASgCGBAQIAEgBTYCGEEBDwsgASgCGCEEIAAoAkwoAhghA0EAIQIDQCAEIAJBNGwiBWoiBCADIAVqKAIkNgIkIAQoAiwQECABKAIYIgQgBWogACgCTCgCGCIDIAVqIgUoAiw2AiwgBUEANgIsIAJBAWoiAiABKAIQSQ0AC0EBDwsgACgCSBAhIABBADYCSEEAC84EAQh/AkAgAkUNAAJAIAAoAqABIgVFDQAgACgCSCIERQ0AIAQoAhBFDQAgBCgCGCgCKCAFRw0AIAIoAhAiCEUNACACKAIYIgYoAigNACAGKAIsDQBBACEEIAhBCE8EQCAIQXhxIQkDQCAGIARBNGxqIAU2AiggBiAEQQFyQTRsaiAFNgIoIAYgBEECckE0bGogBTYCKCAGIARBA3JBNGxqIAU2AiggBiAEQQRyQTRsaiAFNgIoIAYgBEEFckE0bGogBTYCKCAGIARBBnJBNGxqIAU2AiggBiAEQQdyQTRsaiAFNgIoIARBCGohBCAKQQhqIgogCUcNAAsLIAhBB3EiCARAA0AgBiAEQTRsaiAFNgIoIARBAWohBCALQQFqIgsgCEcNAAsLIAIgAxA3DQBBAA8LIAAoAkwiBUUEQCAAQQFBJBATIgU2AkwgBUUNAQsgAiAFED8gACgCwAFBFiADECRFDQAgACgCwAEiBigCACEEIAYoAgghBQJAIAQEQEEBIQcgBEEBcSEIIARBAUYEf0EABSAEQX5xIQlBACEEA0ACf0EAIAdFDQAaQQAgACABIAMgBSgCABEAAEUNABogACABIAMgBSgCBBEAAEEARwshByAFQQhqIQUgBEECaiIEIAlHDQALIAdBAXMLIQQCQAJAIAgEQCAEDQEgACABIAMgBSgCABEAAEEARyEHCyAGQQA2AgAgB0EBcUUNAQwDCyAGQQA2AgALIAAoAkgQISAAQQA2AkhBAA8LIAZBADYCAAsgACACEEchBwsgBwv4BAEGfwJAQQFBMBATIgIEfyACIAAoAsgBIgH9AAMA/QsDACACIAEpAxA3AxAgAiABKAIYIgE2AhggAiABQRhsEBQiATYCHCABRQRAIAIQEEEADwsCQCAAKALIASgCHCIDBEAgASADIAIoAhhBGGwQEhoMAQsgARAQIAJBADYCHAsgAiAAKALIASgCJCIBNgIkIAIgAUEoEBMiATYCKCABRQRAIAIoAhwQECACEBBBAA8LAkAgACgCyAEoAigEQCACKAIkRQ0BA0AgASAFQShsIgNqIAAoAsgBKAIoIANqKAIUIgE2AhQgAUEYbBAUIQEgAigCKCIEIANqIgYgATYCGCABRQRAIAUEf0EAIQEDQCACKAIoIAFBKGxqKAIYEBAgAUEBaiIBIAVHDQALIAIoAigFIAQLEBAMBQsCQCAAKALIASgCKCADaigCGCIEBEAgASAEIAYoAhRBGGwQEhogAigCKCEBDAELIAEQECACKAIoIgEgA2pBADYCGAsgASADaiAAKALIASgCKCADaigCBCIBNgIEIAFBGGwQFCEBIAIoAigiBCADaiIGIAE2AhAgAUUEQCAFBH9BACEBA0AgAUEobCIAIAIoAihqKAIYEBAgAigCKCAAaigCEBAQIAFBAWoiASAFRw0ACyACKAIoBSAECxAQDAULAkAgACgCyAEoAiggA2ooAhAiBARAIAEgBCAGKAIEQRhsEBIaIAIoAighAQwBCyABEBAgAigCKCIBIANqQQA2AhALIAEgA2pCADcCICAFQQFqIgUgAigCJEkNAAsMAQsgARAQIAJBADYCKAsgAgVBAAsPCyACKAIcEBAgAhAQQQALoAYCDn8BeyMAQRBrIggkACAAKAJIKAIQIQ0gCEEBQTgQEyIBNgIMAkAgAUUNACABIAAoAkgoAhAiCTYCGCABIAD9AAJU/QsCACABIAAoAmg2AhAgACgCbCECIAFBADYCNCABIAI2AhQgASAAKAIMIgwoAgA2AiAgASAMKAIENgIkIAEgDCgCCDYCKCABIAwoAhA2AiwgASAJQbgIEBMiADYCMCAABEAgDQRAA0AgDkG4CGwiACABKAIwaiIFIAwoAtArIABqIgT9AAIAIg/9CwIEIAUgBCgCEDYCFCAFIAQoAhQ2AhggD/0bASIAQSBNBEAgBUG0B2ogBEGwB2ogABASGiAFQbAGaiAEQawGaiAEKAIEEBIaCyAFIAQoAhgiADYCHCAFIAQoAqQGNgKoBkEBIQYCQCAAQQFHBEAgBCgCBEEDbCIAQQNrQd8ASw0BIABBAmshBgsgBUGkA2ohCSAFQSBqIQogBEEcaiELQQAhAAJAIAZBCEkNACAEIAZBA3RqQRxqIApLBEAgCyAFIAZBAnRqQaQDakkNAQsgBkF8cSEAQQAhAgNAIAogAkECdCIDaiALIAJBA3RqIgdBHGogB0EUaiAHQQxqIAf9CQIE/VYCAAH9VgIAAv1WAgAD/QsCACADIAlqIAdBGGogB0EQaiAHQQhqIAf9CQIA/VYCAAH9VgIAAv1WAgAD/QsCACACQQRqIgIgAEcNAAsgACAGRg0BCyAAQQFyIQMgBkEBcQRAIAogAEECdCICaiALIABBA3RqIgAoAgQ2AgAgAiAJaiAAKAIANgIAIAMhAAsgAyAGRg0AA0AgCiAAQQJ0IgJqIAsgAEEDdGoiAygCBDYCACACIAlqIAMoAgA2AgAgCiAAQQFqIgNBAnQiAmogCyADQQN0aiIDKAIENgIAIAIgCWogAygCADYCACAAQQJqIgAgBkcNAAsLIAUgBCgCqAY2AqwGIA5BAWoiDiANRw0ACwsgASEDDAELIAhBDGoEQCAIKAIMIgEoAjAiAAR/IAAQECAIKAIMBSABCxAQIAhBADYCDAsLIAhBEGokACADC/kEAQh/IwBBgAJrIgMkACAABEBB/AxBESACEB0gAyAAKAIANgLwASACQZoRIANB8AFqEBYgAyAAKAIENgLgASACQacRIANB4AFqEBYgAyAAKAIINgLQASACQYI3IANB0AFqEBYgAyAAKAIQNgLAASACQf0QIANBwAFqEBYgAUEASgRAA0AgACgC0CshBCADIAc2ArABIAJBog0gA0GwAWoQFiADIAQgB0G4CGxqIgQoAgA2AqABIAJBmREgA0GgAWoQFiADIAQoAgQ2ApABIAJB9DcgA0GQAWoQFiADIAQoAgg2AoABIAJBoDYgA0GAAWoQFiADIAQoAgw2AnAgAkGwNiADQfAAahAWIAMgBCgCEDYCYCACQYgRIANB4ABqEBYgAyAEKAIUNgJQIAJBtjggA0HQAGoQFkHVC0EXIAIQHSAEKAIEBEAgBEGwB2ohBiAEQawGaiEIQQAhBQNAIAggBUECdCIJaigCACEKIAMgBiAJaigCADYCRCADIAo2AkAgAkGLDCADQUBrEBYgBUEBaiIFIAQoAgRJDQALCyACEG4gAyAEKAIYNgIwIAJBwDYgA0EwahAWIAMgBCgCpAY2AiAgAkHxNiADQSBqEBZBASEGQe0LQRQgAhAdAkAgBCgCGEEBRwRAIAQoAgQiBUEATA0BIAVBA2xBAmshBgsgBEEcaiEIQQAhBQNAIAMgCCAFQQN0aikCAEIgiTcDECACQYsMIANBEGoQFiAFQQFqIgUgBkcNAAsLIAIQbiADIAQoAqgGNgIAIAJB4DYgAxAWQZkMQQUgAhAdIAdBAWoiByABRw0ACwtBmgxBBCACEB0LIANBgAJqJAAL5goDCX8BewF+IwBBsAFrIgUkAAJAIAFBgANxBEBBni1BCyACEB0MAQsCQCABQQFxRQ0AIAAoAkgiBkUNACMAQdAAayIDJABB7gxBDSACEB0gA0EAOgBPIANBCToATiADIAYpAgA3AkQgAyADQc4AaiIENgJAIAJBhjkgA0FAaxAWIAMgBikCCDcCNCADIAQ2AjAgAkH1OCADQTBqEBYgAyAGKAIQNgIkIAMgBDYCICACQZM3IANBIGoQFgJAIAYoAhhFDQAgBigCEEUNAANAIAMgA0HOAGoiCjYCECADIAc2AhQgAkGODSADQRBqEBYgBigCGCAHQTRsaiEIIwBBMGsiBCQAIARBCTsALiAEQQk6AC0gBCAIKQIANwIkIAQgBEEtaiIJNgIgIAJBzzYgBEEgahAWIAQgCCgCGDYCFCAEIAk2AhAgAkHFOCAEQRBqEBYgBCAIKAIgNgIEIAQgCTYCACACQao4IAQQFiAEQTBqJAAgAyAKNgIAIAJBlAwgAxAWIAdBAWoiByAGKAIQSQ0ACwtBnAxBAiACEB0gA0HQAGokAAsCQCABQQJxRQ0AIAAoAkhFDQBB+Q1BJCACEB0gBSAAKQJUNwOgASACQecRIAVBoAFqEBYgBSAAKQJcNwOQASACQcURIAVBkAFqEBYgBSAAKQNoNwOAASACQdcRIAVBgAFqEBYgACgCDCAAKAJIKAIQIAIQS0GcDEECIAIQHQsCQCABQQhxRQ0AIAAoAkhFDQAgACgCaCAAKAJsbCIERQ0AIAAoApwBIQMDQCADIAAoAkgoAhAgAhBLIANBjCxqIQMgC0EBaiILIARHDQALCyABQRBxRQ0AIAAoAsgBIQFB0w1BJSACEB0gBSAB/QADAP0LBHAgAkHJKyAFQfAAahAWQcENQREgAhAdAkAgASgCHEUNACABKAIYRQ0AQQAhAwNAIAEoAhwgA0EYbGoiAC8BACEEIAApAwghDSAFIAAoAhA2AmAgBSANNwNYIAUgBDYCUCACQYs4IAVB0ABqEBYgA0EBaiIDIAEoAhhJDQALC0GaDEEEIAIQHQJAIAEoAigiBEUNACABKAIkIgdFDQBBACEDQQAhAAJAIAdBBE8EQCAHQXxxIQADQCAEIANBA3JBKGxqQQRqIAQgA0ECckEobGpBBGogBCADQQFyQShsakEEaiAEIANBKGxq/QkCBP1WAgAB/VYCAAL9VgIAAyAM/a4BIQwgA0EEaiIDIABHDQALIAwgDCAM/Q0ICQoLDA0ODwABAgMAAQID/a4BIgwgDCAM/Q0EBQYHAAECAwABAgMAAQID/a4B/RsAIQMgACAHRg0BCwNAIAQgAEEobGooAgQgA2ohAyAAQQFqIgAgB0cNAAsLIANFDQBBsA1BECACEB0gASgCJARAIAEoAighAEEAIQcDQCAFIAAgB0EobCIEaigCBCIGNgJEIAUgBzYCQCACQdE4IAVBQGsQFiABKAIoIQACQCAGRQ0AQQAhAyAAIARqKAIQRQ0AA0AgASgCKCAEaigCECADQRhsaiIA/QADACEMIAUgACkDEDcDOCAFIAz9CwMoIAUgAzYCICACQaXRACAFQSBqEBYgA0EBaiIDIAZHDQALIAEoAighAAsCQCAAIARqIgYoAhhFDQBBACEDIAYoAhRFDQADQCAAIARqKAIYIANBGGxqIgAvAQAhBiAAKQMIIQ0gBSAAKAIQNgIQIAUgDTcDCCAFIAY2AgAgAkGLOCAFEBYgA0EBaiIDIAEoAigiACAEaigCFEkNAAsLIAdBAWoiByABKAIkSQ0ACwtBmgxBBCACEB0LQZwMQQIgAhAdCyAFQbABaiQAC48CAQN/AkBBAUHoARATIgEEfyABQQE2AgAgAUEBNgK4ASABIAEtALwBQQZyOgC8ASABQQFBjCwQEyIANgIMIABFDQEgAUEBQegHEBMiADYCECAARQ0BIAFCADcDMCABQX82AiwgAUHoBzYCFAJAQQFBMBATIgAEQCAAQQA2AhggAEHkADYCICAAQeQAQRgQEyICNgIcIAINASAAEBALIAFBADYCyAEMAgsgAEEANgIoIAEgADYCyAEgARAzIgA2AsQBIABFDQEgARAzIgA2AsABIABFDQECQBCRAUUNAAsgAUEAEGYiADYC1AEgAEUEQCABQQAQZiIANgLUASAARQ0CCyABBUEACw8LIAEQOEEAC40JAgl/AX4jAEHQAWsiByQAIAAoAkghCQJAAkACQCAAKAJoQQFHDQAgACgCbEEBRw0AIAAoApwBKALcKw0BCyAAKAIIQQhGDQAgBkEBQeHOAEEAEA8MAQsCQCABKAIQIgxFDQAgACgCoAEhCiABKAIYIQsgDEEITwRAIAxBeHEhDwNAIAsgCEE0bGogCjYCKCALIAhBAXJBNGxqIAo2AiggCyAIQQJyQTRsaiAKNgIoIAsgCEEDckE0bGogCjYCKCALIAhBBHJBNGxqIAo2AiggCyAIQQVyQTRsaiAKNgIoIAsgCEEGckE0bGogCjYCKCALIAhBB3JBNGxqIAo2AiggCEEIaiEIIA5BCGoiDiAPRw0ACwsgDEEHcSIMRQ0AA0AgCyAIQTRsaiAKNgIoIAhBAWohCCANQQFqIg0gDEcNAAsLIAIgA3IgBHIgBXJFBEAgBkEEQa8wQQAQDyAAQgA3AhwgACAAKQJoNwIkIAEgCf0AAgD9CwIAIAEgBhA3IQgMAQsgAkEASARAIAcgAjYCACAGQQFBx90AIAcQD0EAIQgMAQsgAiAJKAIIIghLBEAgByAINgIUIAcgAjYCECAGQQFBm+EAIAdBEGoQD0EAIQgMAQsCQCACIAkoAgAiCEkEQCAHIAg2AsQBIAcgAjYCwAEgBkECQfvjACAHQcABahAPIABBADYCHCAJKAIAIQIMAQsgACACIAAoAlRrIAAoAlxuNgIcCyABIAI2AgAgA0EASARAIAcgAzYCICAGQQFBh90AIAdBIGoQD0EAIQgMAQsgAyAJKAIMIgJLBEAgByACNgI0IAcgAzYCMCAGQQFB7t8AIAdBMGoQD0EAIQgMAQsCQCADIAkoAgQiAkkEQCAHIAI2ArQBIAcgAzYCsAEgBkECQcziACAHQbABahAPIABBADYCICAJKAIEIQMMAQsgACADIAAoAlhrIAAoAmBuNgIgCyABIAM2AgRBACEIIARBAEwEQCAHIAQ2AkAgBkEBQcXcACAHQUBrEA8MAQsgBCAJKAIAIgJJBEAgByACNgJUIAcgBDYCUCAGQQFBouMAIAdB0ABqEA8MAQsCQCAEIAkoAggiAksEQCAHIAI2AqQBIAcgBDYCoAEgBkECQcPgACAHQaABahAPIAAgACgCaDYCJCAJKAIIIQQMAQsgACAANQJcIhAgBCAAKAJUa618QgF9IBCAPgIkCyABIAQ2AgggBUEATARAIAcgBTYCYCAGQQFBgtwAIAdB4ABqEA8MAQsgBSAJKAIEIgJJBEAgByACNgJ0IAcgBTYCcCAGQQFB8uEAIAdB8ABqEA8MAQsCQCAFIAkoAgwiAksEQCAHIAI2ApQBIAcgBTYCkAEgBkECQZXfACAHQZABahAPIAAgACgCbDYCKCAJKAIMIQUMAQsgACAANQJgIhAgBSAAKAJYa618QgF9IBCAPgIoCyABIAU2AgwgACAALQBEQQJyOgBEIAEgBhA3IghFBEBBACEIDAELIAcgAf0AAgD9CwSAASAGQQRBtDkgB0GAAWoQDwsgB0HQAWokACAIC5UCAQd/IwBBIGsiBSQAAn8gACgCSCIERQRAIANBAUHF5gBBABAPQQAMAQtBAEEEIAQoAhAQEyIERQ0AGiABBEAgACgCSCEIA0ACQAJAIAIgBkECdGooAgAiByAIKAIQTwRAIAUgBzYCECADQQFB+REgBUEQahAPDAELIAQgB0ECdGoiCSgCAEUNASAFIAc2AgAgA0EBQY0aIAUQDwsgBBAQQQAMAwsgCUEBNgIAIAZBAWoiBiABRw0ACwsgBBAQIAAoAkAQEAJAIAEEQCAAIAFBAnQiBBAUIgM2AkAgA0UEQCAAQQA2AjxBAAwDCyADIAIgBBASGgwBCyAAQQA2AkALIAAgATYCPEEBCyEKIAVBIGokACAKC7wFAQd/IAFBAUEkEBMiBDYCSAJAAkAgBEUNAAJAIAEoAsQBQRIgAxAkBEAgASgCxAFBEyADECQNAQsMAgsgASgCxAEiBygCACEGIAcoAgghBAJAIAYEQEEBIQUgBkEBRwRAIAZBfnEhCQNAAn9BACAFRQ0AGkEAIAEgACADIAQoAgARAABFDQAaIAEgACADIAQoAgQRAABBAEcLIQUgBEEIaiEEIAhBAmoiCCAJRw0ACwsCQAJAIAZBAXEEQCAFRQ0BIAEgACADIAQoAgARAABBAEchBQsgB0EANgIAIAVFDQEMAwsgB0EANgIACwwDCyAHQQA2AgALAkAgASgCwAFBFCADECQEQCABKALAAUEVIAMQJA0BCwwCCyABKALAASIHKAIAIQYgBygCCCEEAkAgBgRAQQEhBSAGQQFxIQkgBkEBRgR/QQAFIAZBfnEhBkEAIQgDQAJ/QQAgBUUNABpBACABIAAgAyAEKAIAEQAARQ0AGiABIAAgAyAEKAIEEQAAQQBHCyEFIARBCGohBCAIQQJqIgggBkcNAAsgBUULIQYCQAJAIAkEQCAGDQEgASAAIAMgBCgCABEAAEEARyEFCyAHQQA2AgAgBUUNAQwDCyAHQQA2AgALDAMLIAdBADYCAAsgAkEBQSQQEyIANgIAIABFDQAgASgCSCAAED8gASgCyAEgASgCbCABKAJobCIANgIkIABBKBATIQMgASgCyAEiACADNgIoAkAgA0UNACAAKAIkRQRAQQEPC0EAIQQDQCADIARBKGwiBWoiAEEANgIUIABB5AA2AhxB5ABBGBATIQAgBSABKALIASIHKAIoIgNqIAA2AhggAEUNAUEBIQogBEEBaiIEIAcoAiRJDQALDAELIAIoAgAQIUEAIQogAkEANgIACyAKDwsgASgCSBAhIAFBADYCSEEACwIACwQAQQELNAACQCAARQ0AIAFFDQAgACABKAIENgKkASAAIAEoAgA2AqABIAAgASgCuEBBAnE2AuABCwu0BQEIfyAAKAIYIgQoAhAiCUUEQEEADwsgBCgCGCEFIAAoAhQoAgAoAhQhBAJAAkAgAUUEQEEAIQEDQCAFKAIYIQIgBCgCHCAEKAIYQZgBbGoiAEGMAWsoAgAiByAAQZQBaygCACIIayEDIABBkAFrKAIAIABBmAFrKAIAayEAAkAgByAIRg0AIACtIAOtfkIgiFANAAwECyAAIANsIQMCQEEEIAJBA3YgAkEHcUEAR2oiACAAQQNGGyICRQ0AIAKtIAOtfkIgiFANAAwEC0F/IQAgAiADbCICIAFBf3NLDQIgBEHMAGohBCAFQTRqIQUgASACaiIBIQAgBkEBaiIGIAlHDQALDAELQQAhASAAKAJARQRAA0AgBSgCGCECIAQoAhwgBCgCGEGYAWxqIgBBBGsoAgAiByAAQQxrKAIAIghrIQMgAEEIaygCACAAQRBrKAIAayEAAkAgByAIRg0AIACtIAOtfkIgiFANAAwECyAAIANsIQMCQEEEIAJBA3YgAkEHcUEAR2oiACAAQQNGGyICRQ0AIAKtIAOtfkIgiFANAAwEC0F/IQAgAiADbCICIAFBf3NLDQIgBEHMAGohBCAFQTRqIQUgASACaiIBIQAgBkEBaiIGIAlHDQALDAELA0AgBSgCGCECIAQoAhwgBCgCGEGYAWxqIgBBjAFrKAIAIgcgAEGUAWsoAgAiCGshAyAAQZABaygCACAAQZgBaygCAGshAAJAIAcgCEYNACAArSADrX5CIIhQDQAMAwsgACADbCEDAkBBBCACQQN2IAJBB3FBAEdqIgAgAEEDRhsiAkUNACACrSADrX5CIIhQDQAMAwtBfyEAIAIgA2wiAiABQX9zSw0BIARBzABqIQQgBUE0aiEFIAEgAmoiASEAIAZBAWoiBiAJRw0ACwsgAA8LQX8L2gQBC38gAARAIAAoAhQiAQRAIAEoAgAiBQRAIAUoAhQhAyAFKAIQBH9BEEERIAAtAChBAXEbIQgDQCADKAIcIgIEQCADKAIgIgFBmAFuIQpBACEJIAFBmAFPBH8DQCACKAIwIgEEQCACKAI0IgZBKG4hB0EAIQQgBkEoTwR/A0AgASgCIBApIAFBADYCICABKAIkECkgAUEANgIkIAEgCBECACABQShqIQEgBEEBaiIEIAdHDQALIAIoAjAFIAELEBAgAkEANgIwCyACKAJUIgEEQCACKAJYIgZBKG4hB0EAIQQgBkEoTwR/A0AgASgCIBApIAFBADYCICABKAIkECkgAUEANgIkIAEgCBECACABQShqIQEgBEEBaiIEIAdHDQALIAIoAlQFIAELEBAgAkEANgJUCyACKAJ4IgEEQCACKAJ8IgZBKG4hB0EAIQQgBkEoTwR/A0AgASgCIBApIAFBADYCICABKAIkECkgAUEANgIkIAEgCBECACABQShqIQEgBEEBaiIEIAdHDQALIAIoAngFIAELEBAgAkEANgJ4CyACQZgBaiECIAlBAWoiCSAKRw0ACyADKAIcBSACCxAQIANBADYCHAsCQCADKAIoRQ0AIAMoAiQiAUUNACABEBAgA/0MAAAAAAAAAAAAAAAAAAAAAP0LAiQLIAMoAjQQECADQcwAaiEDIAtBAWoiCyAFKAIQSQ0ACyAFKAIUBSADCxAQIAVBADYCFCAAKAIUKAIAEBAgACgCFCIBQQA2AgALIAEQECAAQQA2AhQLIAAoAkQQECAAEBALC8sTARV/IwBBIGsiDyQAIA8gBTYCGCABIAMoAhxBzABsaigCHCADKAIgQZgBbGohEQJAAkAgAygCKA0AIBEoAhhFDQAgEUEcaiEJA0ACQCAJKAIIIAkoAgBHBH8gCSgCDCAJKAIERgVBAQsNACADKAIkIgEgCSgCGEEobk8EQCAIQQFBghVBABAPDAQLIAkoAhQgAUEobGoiASgCIBBiIAEoAiQQYiABKAIUIAEoAhBsIg1FDQAgASgCGCEBIA1BCE8EQCANQXhxIQtBACEKA0AgAUIANwLoAyABQgA3AqgDIAFCADcC6AIgAUIANwKoAiABQgA3AugBIAFCADcCqAEgAUIANwJoIAFCADcCKCABQYAEaiEBIApBCGoiCiALRw0ACwtBACEKIA1BB3EiDUUNAANAIAFCADcCKCABQUBrIQEgCkEBaiIKIA1HDQALCyAJQSRqIQkgDEEBaiIMIBEoAhhJDQALCyAFIQ0CQCACLQAAQQJxRQ0AIAdBBU0EQCAIQQJBsR9BABAPDAELAkAgBS0AAEH/AUYEQCAFLQABQZEBRg0BCyAIQQJB2x9BABAPDAELIA8gBUEGaiINNgIYC0EUEBQiC0UNAAJ/IAAtAGxBAXEEQCAAQShqIQcgACgCKCENIABBLGoMAQsgAi0AiCxBAnEEQCACQbAoaiEHIAIoArAoIQ0gAkG8KGoMAQsgDyAFIAdqIA1rNgIcIA9BGGohByAPQRxqCyISKAIAIQAgC0IANwIMIAsgDTYCCCALIA02AgAgCyAAIA1qNgIEIAtBARAfRQRAIAsQZBogCygCCCALKAIAayEaIAsQLCAaIA1qIQECQCACLQAAQQRxRQ0AIAcoAgAgEigCACABa2pBAU0EQCAIQQJBmCFBABAPDAELAkAgAS0AAEH/AUYEQCABLQABQZIBRg0BCyAIQQJBwiFBABAPDAELIAFBAmohAQsgEiASKAIAIAcoAgAgAWtqNgIAIAcgATYCACAEQQA2AgAgBiAPKAIYIAVrNgIAQQEhFwwBCyARKAIYBEAgEUEcaiEQA0AgAygCJCEAIBAoAhQhAQJAIBAoAgggECgCAEcEfyAQKAIMIBAoAgRGBUEBCw0AIAEgAEEobGoiFCgCFCAUKAIQbCIYRQ0AIBQoAhghCUEAIRUDQAJAAn8gCSgCKEUEQCALIBQoAiAgFSADKAIoQQFqEGAMAQsgC0EBEB8LRQRAIAlBADYCJAwBCyAJKAIoRQRAQQAhAQNAIAEiAEEBaiEBIAsgFCgCJCAVIAAQYEUNAAsgECgCHCEBIAlBAzYCICAJIAE2AhggCSABIABrQQFqNgIcCyAJAn9BASALQQEQH0UNABpBAiALQQEQH0UNABogC0ECEB8iAEEDRwRAIABBA2oMAQsgC0EFEB8iAEEfRwRAIABBBmoMAQsgC0EHEB9BJWoLNgIkQQAhAQNAIAEiAEEBaiEBIAtBARAfDQALIAkgCSgCICAAajYCIAJAAkACfyAJKAIoIgBFBEAgAigC0CsgAygCHEG4CGxqKAIQIQAgCSgCMEUEQCAJKAIAQfABEBciAUUNBCAJIAE2AgAgASAJKAIwQRhsakEAQfABEBUaIAlBCjYCMAsgCSgCACIB/QwAAAAAAAAAAAAAAAAAAAAA/QsCACABQgA3AhBBAUEKQe0AIABBAXEbIABBBHEbIQpBAAwBCyAJKAIAIgEgAEEBayIMQRhsaiIKKAIEIAooAgxHDQEgAigC0CsgAygCHEG4CGxqKAIQIQogCSgCMCIMIABBAWpJBH8gASAMQQpqIgxBGGwQFyIBRQ0DIAkgATYCACABIAkoAjBBGGxqQQBB8AEQFRogCSAMNgIwIAkoAgAFIAELIABBGGxqIgH9DAAAAAAAAAAAAAAAAAAAAAD9CwIAIAFCADcCEAJ/QQEgCkEEcQ0AGkHtACAKQQFxRQ0AGkECQQJBASABQQxrKAIAIgpBCkYbIApBAUYbCyEKIAALIQwgASAKNgIMCyAJKAIkIQAgAigC0CsgAygCHEG4CGxqLQAQQcAAcQRAA0AgDEEYbCIOIAkoAgBqIABBASAMGyITNgIQIAkoAiAhFkEAIQogACEBIBNBAk8EQANAIApBAWohCiABQQNLIRsgAUEBdiEBIBsNAAsLIAogFmoiAUEhTwRAIA8gATYCECAIQQFBvPQAIA9BEGoQDwwDCyALIAEQHyEKIAkoAgAiASAOaiIOIAo2AhQgACAOKAIQayIAQQBMDQMgAigC0CsgAygCHEG4CGxqKAIQIQogCSgCMCIOIAxBAmpJBEAgASAOQQpqIg5BGGwQFyIBRQ0DIAkgATYCACABIAkoAjBBGGxqQQBB8AEQFRogCSAONgIwIAkoAgAhAQsgASAMQQFqIgxBGGxqIgH9DAAAAAAAAAAAAAAAAAAAAAD9CwIAIAFCADcCECABAn9BASAKQQRxDQAaQe0AIApBAXFFDQAaQQJBAkEBIAFBDGsoAgAiAUEKRhsgAUEBRhsLNgIMDAALAAsDQCAMQRhsIg4gCSgCAGoiASABKAIMIAEoAgRrIgEgACAAIAFKGyIBNgIQIAkoAiAhE0EAIQogAUECTwRAA0AgCkEBaiEKIAFBA0shHCABQQF2IQEgHA0ACwsgCiATaiIBQSFPBEAgDyABNgIAIAhBAUG89AAgDxAPDAILIAsgARAfIQogCSgCACIBIA5qIg4gCjYCFCAAIA4oAhBrIgBBAEwNAiACKALQKyADKAIcQbgIbGooAhAhCiAJKAIwIg4gDEECakkEQCABIA5BCmoiDkEYbBAXIgFFDQIgCSABNgIAIAEgCSgCMEEYbGpBAEHwARAVGiAJIA42AjAgCSgCACEBCyABIAxBAWoiDEEYbGoiAf0MAAAAAAAAAAAAAAAAAAAAAP0LAgAgAUIANwIQIAECf0EBIApBBHENABpB7QAgCkEBcUUNABpBAkECQQEgAUEMaygCACIBQQpGGyABQQFGGws2AgwMAAsACyALECwMBQsgCUFAayEJIBVBAWoiFSAYRw0ACwsgEEEkaiEQIBlBAWoiGSARKAIYSQ0ACwsgCxBkRQRAIAsQLAwBCyALKAIIIAsoAgBrIR0gCxAsIB0gDWohAQJAIAItAABBBHFFDQAgBygCACASKAIAIAFrakEBTQRAIAhBAkGYIUEAEA8MAQsCQCABLQAAQf8BRgRAIAEtAAFBkgFGDQELIAhBAkHCIUEAEA8MAQsgAUECaiEBCyASIBIoAgAgBygCACABa2o2AgAgByABNgIAQQEhFyAEQQE2AgAgBiAPKAIYIAVrNgIACyAPQSBqJAAgFwuWJAIUfw5+AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAJUDgUAAQIDBAoLAkAgACgCNCIGIAAoAsQBIgFJBEAgACgCQCIHIAFBAWpJDQELIAAoAuwBQQFB9D9BABAPDAwLIAAoAixFBEAgACgCJCECQQAhAQwFCyAAQQA2AiwgACgCRCEDQQEhAQwECwJAIAAoAjQiBiAAKALEASIBSQRAIAAoAkAiByABQQFqSQ0BCyAAKALsAUEBQaHAAEEAEA8MCwsgACgCLEUEQCAAKAIkIQRBACEBDAgLIABBADYCLCAAKAIwIQNBASEBDAcLAkAgACgCNCIEIAAoAsQBIgpJBEAgACgCQCIOIApBAWpJDQELIAAoAuwBQQFBqMEAQQAQDwwKCyAAKAIsRQRAIAAoAighCwwGCyAAQgA3AuQBIABBADYCLCAAKALIASEMA0AgDCAHQQR0aiIFKAIIIg8EQCAFKAIMIRJBACEBA0ACQCAPIAFBf3NqIhAgEiABQQR0aiIRKAIAaiIJQR9LDQAgBSgCACITQX8gCXZLDQAgACACIBMgCXQiCSACIAlJGyAJIAIbIgI2AuQBCwJAIBEoAgQgEGoiCUEfSw0AIAUoAgQiEEF/IAl2Sw0AIAAgAyAQIAl0IgkgAyAJSRsgCSADGyIDNgLoAQsgAUEBaiIBIA9HDQALCyAHQQFqIgcgCkcNAAsgAkUNByADRQ0HIAAtAABFBEAgACAAKALQATYCbCAAIAAoAswBNgJkIAAgACgC2AE2AnAgACAAKALUATYCaAsgACgCMCEFQQEhAQwFCwJAIAAoAjQiBSAAKALEASIJSQRAIAAoAkAiEiAJQQFqSQ0BCyAAKALsAUEBQfvAAEEAEA8MCQsgACgCLEUEQCAAKALIASINIAAoAhwiBEEEdGohCyAAKAIoIQgMBAsgAEIANwLkASAAQQA2AiwgACgCyAEhDQNAIA0gBkEEdGoiCigCCCIOBEAgCigCDCEQQQAhAQNAAkAgDiABQX9zaiIRIBAgAUEEdGoiEygCAGoiDEEfSw0AIAooAgAiFEF/IAx2Sw0AIAAgAiAUIAx0IgwgAiAMSRsgDCACGyICNgLkAQsCQCATKAIEIBFqIgxBH0sNACAKKAIEIhFBfyAMdksNACAAIAMgESAMdCIMIAMgDEkbIAwgAxsiAzYC6AELIAFBAWoiASAORw0ACwsgBkEBaiIGIAlHDQALIAJFDQYgA0UNBgJAIAAtAAAEQCAAKAJsIQYMAQsgACAAKALQASIGNgJsIAAgACgCzAE2AmQgACAAKALYATYCcCAAIAAoAtQBNgJoC0EBIQEMAwsCQCAAKAI0IgYgACgCxAEiAUkEQCAAKAJAIg8gAUEBakkNAQsgACgC7AFBAUHOwABBABAPDAYLIAAoAixFBEAgACgCyAEgACgCHCIGQQR0aiEFIAAoAighB0EAIQEMAgsgACAGNgIcIABBADYCLEEBIQEMAQsDQAJ/AkAgAUUEQCACQQFqIQIMAQsgACADNgIoIAAoAjggA00NCSAAKAIwIQRBAAwBC0EBCyEBA0ACQAJAAkACQCABRQRAIAAgBDYCICAEIAAoAjxPDQEgACAGNgIcIAYhAUEAIQUMBAsgACACNgIkIAAoAkwgAk0EQCAAKAIcIQFBASEFDAQLIAAoAhAgACgCIGwgACgCDCAAKAIobGogACgCFCAAKAIcbGogACgCGCACbGoiASAAKAIITwRADAwLIAAoAgQgAUEBdGoiAS8BAA0BDA0LIAAoAihBAWohAwwBC0EAIQEMAwtBASEBDAILA0ACQAJAAkAgBUUEQCABIAdPDQEgACgCICIFIAAoAsgBIAFBBHRqIg0oAghPDQMgAC0AAEUEQCAAIA0oAgwgBUEEdGoiASgCDCABKAIIbDYCTAsgACgCSCECQQEhAQwFCyAAIAFBAWoiATYCHAwBCyAAKAIgQQFqIQRBACEBDAMLQQAhBQwBC0EBIQUMAAsACwALAAsDQAJ/AkAgAUUEQCAAIAdBAWoiBzYCKAwBCyAGIA9PDQggAEIANwLkASAAKALIASAGQQR0aiIFKAIIIgtFDQggBSgCDCEKQQAhAkEAIQRBACEBA0ACQCALIAFBf3NqIgkgCiABQQR0aiIOKAIAaiIIQR9LDQAgBSgCACIMQX8gCHZLDQAgACAEIAwgCHQiCCAEIAhJGyAIIAQbIgQ2AuQBCwJAIA4oAgQgCWoiCEEfSw0AIAUoAgQiCUF/IAh2Sw0AIAAgAiAJIAh0IgggAiAISRsgCCACGyICNgLoAQsgAUEBaiIBIAtHDQALIARFDQYgAkUNBgJAIAAtAAAEQCAAKAJsIQIMAQsgACAAKALQASICNgJsIAAgACgCzAE2AmQgACAAKALYATYCcCAAIAAoAtQBNgJoC0EADAELQQELIQEDQAJAAkACQAJAIAFFBEAgACACNgLgASACIAAoAnBPDQEgACgCZCENQQAhAQwECyAAKAI4IAdNBEAgACgCICEDQQEhAQwECyAAKAIQIAAoAiBsIAAoAgwgB2xqIAAoAhQgBmxqIAAoAhggACgCJGxqIgEgACgCCE8EQAwLCyAAKAIEIAFBAXRqIgEvAQANAQwMCyAAIAZBAWoiBjYCHAwBC0EAIQEMAwtBASEBDAILA0ACQAJAAkAgAAJ/IAFFBEAgACANNgLcASANIAAoAmhPDQIgACgCMAwBCyADQQFqCyIDNgIgIAAoAjwiASAFKAIIIgQgASAESRsgA0sEQCAFKAIAIgEgAa0iHiAEIANBf3NqIgitIhaGIhcgFoinRw0DIAUoAgQiBEJ/IBaIp3EgBEcNAyAErSIVIBaGIhhCAX0iGSAANQLYAXwgGIAhHyAZIAAoAtABIgmtfCAYgCEaIBdCAX0iGyAANQLUAXwgF4AhICAbIAAoAswBIg6tfCAXgCEcIAFCfyAFKAIMIANBBHRqIgsoAgAiCiAIaq0iHYincSABRw0DIAQgFSALKAIEIgEgCGqtIhWGIiEgFYinRw0DIAAoAuABIgStIiIgIYJCAFIEQCAEIAlHDQRCfyAVhkJ/hSAaQv////8PgyAWhoNQDQQLIAAoAtwBIgStIhUgHiAdhoJCAFIEQCAEIA5HDQRCfyAdhkJ/hSAcQv////8PgyAWhoNQDQQLIAsoAggiBEUNAyALKAIMRQ0DIBynIgsgIKdGDQMgGqciCCAfp0YNAyAAIAAoAkQiBzYCKCAAIBUgG3wgF4CnIAp2IAsgCnZrIBkgInwgGICnIAF2IAggAXZrIARsajYCJEEBIQEMBQsgACgC3AEiASAAKALkASIEaiABIARwayENDAELIAAoAuABIgEgACgC6AEiBGogASAEcGshAkEAIQEMAwtBACEBDAELQQEhAQwACwALAAsACwNAAn8CQCABRQRAIAAgCEEBaiIINgIoDAELIAAgBjYC4AEgACgCcCAGTQ0HIAAoAmQhD0EADAELQQELIQEDQAJAAkACQAJAIAFFBEAgACAPNgLcASAPIAAoAmhPDQEgACAFNgIcIAUhBEEAIQEMBAsgACgCOCAITQRAIAAoAiAhB0EBIQEMBAsgACgCECAAKAIgbCAAKAIMIAhsaiAAKAIUIARsaiAAKAIYIAAoAiRsaiIBIAAoAghPBEAMCgsgACgCBCABQQF0aiIBLwEADQEMCwsgACgC4AEiASAAKALoASIGaiABIAZwayEGDAELQQAhAQwDC0EBIQEMAgsDQAJAAkACQAJAIAFFBEAgBCASTw0CIAAgACgCMCIHNgIgIA0gBEEEdGohCwwBCyAAIAdBAWoiBzYCIAsgACgCPCIBIAsoAggiAiABIAJJGyAHSwRAIAsoAgAiASABrSIeIAIgB0F/c2oiCq0iFoYiFyAWiKdHDQMgCygCBCICQn8gFoincSACRw0DIAKtIhUgFoYiGEIBfSIZIAA1AtgBfCAYgCEfIBkgACgC0AEiDq18IBiAIRogF0IBfSIbIAA1AtQBfCAXgCEgIBsgACgCzAEiDK18IBeAIRwgAUJ/IAsoAgwgB0EEdGoiAygCACIJIApqrSIdiKdxIAFHDQMgAiAVIAMoAgQiASAKaq0iFYYiISAViKdHDQMgACgC4AEiAq0iIiAhgkIAUgRAIAIgDkcNBEJ/IBWGQn+FIBpC/////w+DIBaGg1ANBAsgACgC3AEiAq0iFSAeIB2GgkIAUgRAIAIgDEcNBEJ/IB2GQn+FIBxC/////w+DIBaGg1ANBAsgAygCCCICRQ0DIAMoAgxFDQMgHKciAyAgp0YNAyAapyIKIB+nRg0DIAAgACgCRCIINgIoIAAgFSAbfCAXgKcgCXYgAyAJdmsgGSAifCAYgKcgAXYgCiABdmsgAmxqNgIkQQEhAQwFCyAAIARBAWoiBDYCHAwBCyAAKALcASIBIAAoAuQBIgJqIAEgAnBrIQ9BACEBDAMLQQAhAQwBC0EBIQEMAAsACwALAAsDQAJ/AkAgAUUEQCAAIAtBAWoiCzYCKAwBCyAAIAU2AiAgACgCPCAFTQ0GIAAoAmwhCEEADAELQQELIQEDQAJAAkACQAJAIAFFBEAgACAINgLgASAIIAAoAnBPDQEgACgCZCENQQAhAQwECyAAKAI4IAtNBEAgACgCHCEGQQEhAQwECyAAKAIQIAAoAiBsIAAoAgwgC2xqIAAoAhQgACgCHGxqIAAoAhggACgCJGxqIgEgACgCCE8EQAwJCyAAKAIEIAFBAXRqIgEvAQANAQwKCyAAKAIgQQFqIQUMAQtBACEBDAMLQQEhAQwCCwNAAkACQAJAAkAgAUUEQCAAIA02AtwBIA0gACgCaE8NAiAAIAQ2AhwgBCEGDAELIAAgBkEBaiIGNgIcCyAGIA5JBEAgACgCICIHIAAoAsgBIAZBBHRqIgEoAggiA08NAyABKAIAIgIgAq0iHiADIAdBf3NqIgqtIhaGIhcgFoinRw0DIAEoAgQiA0J/IBaIp3EgA0cNAyADrSIVIBaGIhhCAX0iGSAANQLYAXwgGIAhHyAZIAAoAtABIg+tfCAYgCEaIBdCAX0iGyAANQLUAXwgF4AhICAbIAAoAswBIgmtfCAXgCEcIAJCfyABKAIMIAdBBHRqIgEoAgAiByAKaq0iHYincSACRw0DIAMgFSABKAIEIgIgCmqtIhWGIiEgFYinRw0DIAAoAuABIgOtIiIgIYJCAFIEQCADIA9HDQRCfyAVhkJ/hSAaQv////8PgyAWhoNQDQQLIAAoAtwBIgOtIhUgHiAdhoJCAFIEQCADIAlHDQRCfyAdhkJ/hSAcQv////8PgyAWhoNQDQQLIAEoAggiA0UNAyABKAIMRQ0DIBynIgEgIKdGDQMgGqciCiAfp0YNAyAAIAAoAkQiCzYCKCAAIBUgG3wgF4CnIAd2IAEgB3ZrIBkgInwgGICnIAJ2IAogAnZrIANsajYCJEEBIQEMBQsgACgC3AEiASAAKALkASICaiABIAJwayENDAELIAAoAuABIgEgACgC6AEiAmogASACcGshCEEAIQEMAwtBACEBDAELQQEhAQwACwALAAsACwNAAn8CQCABRQRAIARBAWohBAwBCyAAIAM2AiAgACgCPCADTQ0FIAAoAkQhAkEADAELQQELIQEDQAJAAkACQAJAIAFFBEAgACACNgIoIAIgACgCOE8NASAAIAY2AhwgBiEBQQAhBQwECyAAIAQ2AiQgACgCTCAETQRAIAAoAhwhAUEBIQUMBAsgACgCECAAKAIgbCAAKAIMIAAoAihsaiAAKAIUIAAoAhxsaiAAKAIYIARsaiIBIAAoAghPBEAMCAsgACgCBCABQQF0aiIBLwEADQEMCQsgACgCIEEBaiEDDAELQQAhAQwDC0EBIQEMAgsDQAJAAkACQCAFRQRAIAEgB08NASAAKAIgIgUgACgCyAEgAUEEdGoiDSgCCE8NAyAALQAARQRAIAAgDSgCDCAFQQR0aiIBKAIMIAEoAghsNgJMCyAAKAJIIQRBASEBDAULIAAgAUEBaiIBNgIcDAELIAAoAihBAWohAkEAIQEMAwtBACEFDAELQQEhBQwACwALAAsAC0EADwsgACgC7AFBAUGaCkEAEA8LQQAPCyABQQE7AQBBAQuRCwEKfwJAIAEoAgAgBEEDbCIMdiIGQZCAgAFxDQAgACAAQRxqIg4gACgCbCAGQe8DcWotAABBAnRqIgo2AmggACAAKAIEIAooAgAiCSgCACIIayIGNgIEAkAgCCAAKAIAIgdBEHZLBEAgCSgCBCELIAAgCDYCBCAKIAlBCEEMIAYgCEkiBhtqKAIANgIAIAsgC0UgBhshCSAAKAIIIQYDQAJAIAYNACAAKAIQIgZBAWohCyAGLQABIQogBi0AAEH/AUYEQCAKQZABTwRAIAAgACgCDEEBajYCDCAHQYD+A2ohB0EIIQYMAgsgACALNgIQIAcgCkEJdGohB0EHIQYMAQsgACALNgIQQQghBiAHIApBCHRqIQcLIAAgBkEBayIGNgIIIAAgB0EBdCIHNgIAIAAgCEEBdCIINgIEIAhBgIACSQ0ACyAIIQYMAQsgACAHIAhBEHRrIgc2AgAgBkGAgAJxRQRAIAkoAgQhCyAKIAlBDEEIIAYgCEkiCBtqKAIANgIAIAtFIAsgCBshCSAAKAIIIQgDQAJAIAgNACAAKAIQIghBAWohCyAILQABIQogCC0AAEH/AUYEQCAKQZABTwRAIAAgACgCDEEBajYCDCAHQYD+A2ohB0EIIQgMAgsgACALNgIQIAcgCkEJdGohB0EHIQgMAQsgACALNgIQQQghCCAHIApBCHRqIQcLIAAgCEEBayIINgIIIAAgB0EBdCIHNgIAIAAgBkEBdCIGNgIEIAZBgIACSQ0ACwwBCyAJKAIEIQkLIAlFDQAgACAOIAEoAgQgDEERanZBBHEgAUEEayINKAIAIAxBE2p2QQFxIAEoAgAiCCAMQRBqdkHAAHEgCCAMdkGqAXFyIAggDEEMakEOIAQbdkEQcXJyciIPQdC5AWotAABBAnRqIgs2AmggACAGIAsoAgAiCigCACIIayIGNgIEAkAgCCAHQRB2SwRAIAooAgQhCSAAIAg2AgQgCyAKQQhBDCAGIAhJIgYbaigCADYCACAJIAlFIAYbIQogACgCCCEGA0ACQCAGDQAgACgCECIGQQFqIQsgBi0AASEJIAYtAABB/wFGBEAgCUGQAU8EQCAAIAAoAgxBAWo2AgwgB0GA/gNqIQdBCCEGDAILIAAgCzYCECAHIAlBCXRqIQdBByEGDAELIAAgCzYCEEEIIQYgByAJQQh0aiEHCyAAIAZBAWsiBjYCCCAAIAdBAXQiBzYCACAAIAhBAXQiCDYCBCAIQYCAAkkNAAsMAQsgACAHIAhBEHRrIgk2AgAgBkGAgAJxRQRAIAooAgQhByALIApBDEEIIAYgCEkiCBtqKAIANgIAIAdFIAcgCBshCiAAKAIIIQcDQAJAIAcNACAAKAIQIgdBAWohCyAHLQABIQggBy0AAEH/AUYEQCAIQZABTwRAIAAgACgCDEEBajYCDCAJQYD+A2ohCUEIIQcMAgsgACALNgIQIAkgCEEJdGohCUEHIQcMAQsgACALNgIQQQghByAJIAhBCHRqIQkLIAAgB0EBayIHNgIIIAAgCUEBdCIJNgIAIAAgBkEBdCIGNgIEIAZBgIACSQ0ACwwBCyAKKAIEIQoLIAJBACADayADIAogD0HQuwFqLQAAcyIDGzYCACANIA0oAgBBICAMdHI2AgAgASABKAIAIANBE3RBEHIgDHRyNgIAIAEgASgCBEEIIAx0cjYCBCAEIAVyRQRAIAFBfiAAKAJ8a0ECdGoiAiACKAIEQYCAAnI2AgQgAiACKAIAIANBH3RyQYCABHI2AgAgAkEEayICIAIoAgBBgIAIcjYCAAsgBEEDRw0AIAEgACgCfEECdGoiAEEEaiAAKAIEQQRyNgIAIAAgACgCDEEBcjYCDCAAIAAoAgggA0ESdHJBAnI2AggLC6sLAQl/AkAgASgCACAEQQNsIg12IgdBkICAAXENACAHQe8DcSIHRQ0AIAAgAEEcaiIOIAAoAmwgB2otAABBAnRqIgs2AmggACAAKAIEIAsoAgAiCigCACIJayIHNgIEAkAgCSAAKAIAIghBEHZLBEAgCigCBCEMIAAgCTYCBCALIApBCEEMIAcgCUkiBxtqKAIANgIAIAwgDEUgBxshCiAAKAIIIQcDQAJAIAcNACAAKAIQIgdBAWohDCAHLQABIQsgBy0AAEH/AUYEQCALQZABTwRAIAAgACgCDEEBajYCDCAIQYD+A2ohCEEIIQcMAgsgACAMNgIQIAggC0EJdGohCEEHIQcMAQsgACAMNgIQQQghByAIIAtBCHRqIQgLIAAgB0EBayIHNgIIIAAgCEEBdCIINgIAIAAgCUEBdCIJNgIEIAlBgIACSQ0ACyAJIQcMAQsgACAIIAlBEHRrIgg2AgAgB0GAgAJxRQRAIAooAgQhDCALIApBDEEIIAcgCUkiCRtqKAIANgIAIAxFIAwgCRshCiAAKAIIIQkDQAJAIAkNACAAKAIQIglBAWohDCAJLQABIQsgCS0AAEH/AUYEQCALQZABTwRAIAAgACgCDEEBajYCDCAIQYD+A2ohCEEIIQkMAgsgACAMNgIQIAggC0EJdGohCEEHIQkMAQsgACAMNgIQQQghCSAIIAtBCHRqIQgLIAAgCUEBayIJNgIIIAAgCEEBdCIINgIAIAAgB0EBdCIHNgIEIAdBgIACSQ0ACwwBCyAKKAIEIQoLAkAgCkUNACAAIA4gASgCBCANQRFqdkEEcSABQQRrIg8oAgAgDUETanZBAXEgASgCACIJIA1BEGp2QcAAcSAJIA12QaoBcXIgCSANQQxqQQ4gBBt2QRBxcnJyIgpB0LkBai0AAEECdGoiDDYCaCAAIAcgDCgCACILKAIAIglrIgc2AgQgCkHQuwFqLQAAIQ4CQCAJIAhBEHZLBEAgCygCBCEKIAAgCTYCBCAMIAtBCEEMIAcgCUkiBxtqKAIANgIAIAogCkUgBxshCyAAKAIIIQcDQAJAIAcNACAAKAIQIgdBAWohDCAHLQABIQogBy0AAEH/AUYEQCAKQZABTwRAIAAgACgCDEEBajYCDCAIQYD+A2ohCEEIIQcMAgsgACAMNgIQIAggCkEJdGohCEEHIQcMAQsgACAMNgIQQQghByAIIApBCHRqIQgLIAAgB0EBayIHNgIIIAAgCEEBdCIINgIAIAAgCUEBdCIJNgIEIAlBgIACSQ0ACwwBCyAAIAggCUEQdGsiCjYCACAHQYCAAnFFBEAgCygCBCEIIAwgC0EMQQggByAJSSIJG2ooAgA2AgAgCEUgCCAJGyELIAAoAgghCANAAkAgCA0AIAAoAhAiCEEBaiEMIAgtAAEhCSAILQAAQf8BRgRAIAlBkAFPBEAgACAAKAIMQQFqNgIMIApBgP4DaiEKQQghCAwCCyAAIAw2AhAgCiAJQQl0aiEKQQchCAwBCyAAIAw2AhBBCCEIIAogCUEIdGohCgsgACAIQQFrIgg2AgggACAKQQF0Igo2AgAgACAHQQF0Igc2AgQgB0GAgAJJDQALDAELIAsoAgQhCwsgAkEAIANrIAMgCyAOcyICGzYCACAPIA8oAgBBICANdHI2AgAgASABKAIAIAJBE3RBEHIgDXRyNgIAIAEgASgCBEEIIA10cjYCBCAEIAZyRQRAIAEgBUECdGsiACAAKAIEQYCAAnI2AgQgACAAKAIAIAJBH3RyQYCABHI2AgAgAEEEayIAIAAoAgBBgIAIcjYCAAsgBEEDRw0AIAEgBUECdGoiACAAKAIEQQFyNgIEIAAgACgCACACQRJ0ckECcjYCACAAQQRrIgAgACgCAEEEcjYCAAsgASABKAIAQYCAgAEgDXRyNgIACwutAQAgAEHwnQE2AmQgAEHwnQE2AmAgAEHwnQE2AlwgAEHwnQE2AlggAEHwnQE2AlQgAEHwnQE2AlAgAEHwnQE2AkwgAEHwnQE2AkggAEHwnQE2AkQgAEHwnQE2AkAgAEHwnQE2AjwgAEHwnQE2AjggAEHwnQE2AjQgAEHwnQE2AjAgAEHwnQE2AiwgAEHwnQE2AiggAEHwnQE2AiQgAEHwnQE2AiAgAEHwnQE2AhwLkgYCCX8EfiAAIAE2AgAgAP0MAAAAAAAAAAAAAAAAAAAAAP0LAwggACADNgIcIAAgAkEBayIFNgIYIAFBA3EhCgJ/IAJBAEwEQCABIQQgAwwBCyAAIAFBAWoiBDYCACABLQAACyEBQQghByAAQQg2AhAgACABrSINNwMIIAAgDUL/AYMiDkL/AVEiCTYCFAJAIApBA0YNACAAIAJBAmsiCDYCGAJ/IAJBAkgEQCAEIQEgAwwBCyAAIARBAWoiATYCACAELQAACyEEIABBD0EQIA5C/wFRGyIHNgIQIAAgBK0iDkL/AYMiD0L/AVEiCTYCFCAAIA5CCIYgDYQiDTcDCCAKQQJGBEAgASEEIAUhAiAIIQUMAQsgACACQQNrIgs2AhggAAJ/IAJBA0gEQCABIQYgAwwBCyAAIAFBAWoiBjYCACABLQAAC60iDkL/AYMiEEL/AVEiCTYCFCAAQQdBCCAPQv8BURsgB2oiATYCECAAIA4gB62GIA2EIg03AwggCkEBRgRAIAYhBCABIQcgCCECIAshBQwBCyAAIAJBBGsiBTYCGCAAAn8gAkEESARAIAYhBCADDAELIAAgBkEBaiIENgIAIAYtAAALrSIOQv8Bg0L/AVEiCTYCFCAAQQdBCCAQQv8BURsgAWoiBzYCECAAIA4gAa2GIA2EIg03AwggCyECCwJAIAJBBU4EQCAEKAIAIQMgACACQQVrNgIYIAAgBEEEajYCAAwBC0EAIQFBf0EAIAMbIQMgAkECSA0AA0AgACAEQQFqIgI2AgAgBC0AACEEIAAgBUEBayIGNgIYIANB/wEgAXRBf3NxIAQgAXRyIQMgAUEIaiEBIAVBAUshDCACIQQgBiEFIAwNAAsLIAAgA0EYdiIBQf8BRjYCFCAAQQdBCCAJGyICQQdBCCADQf8BcSIEQf8BRhtqIgVBB0EIIANBCHZB/wFxIgZB/wFGG2oiCEEHQQggA0EQdkH/AXEiA0H/AUYbIAdqajYCECAAIAYgAnQgAyAFdHIgASAIdHIgBHKtIAethiANhDcDCAu2BQISfwJ+An8gACgCHCABQZgBbGoiAkGQAWsoAgAgAkGYAWsoAgBrIgMhBSACQYwBaygCACACQZQBaygCAGsiAiEGQcAAIAMgA0HAAE8bIQNBwAAgAiACQcAATxshBAJAIAVFDQAgBkUNACADRQ0AIARFDQBBfyAEbkECdiADSQ0AQQFBHBATIgIgBDYCDCACIAM2AgggAiAGNgIEIAIgBTYCACACIAStIhQgBq18QgF9IBSAIhSnIgQ2AhQgAiADrSIVIAWtfEIBfSAVgCIVpyIDNgIQAkAgFEL/////D4MgFUL/////D4N+QiCIpw0AIAJBBCADIARsEBMiAzYCGCADRQ0AIAIMAgsgAhAQC0EACyIJRQRAQQAPCwJAIAEEQANAIA5BmAFsIg8gACgCHGoiBSgCGCICBEAgBUEcaiEQIAUoAhQhAyAFKAIQIQRBACEKA0AgAyAEbARAIBAgCkEkbGohBkEAIQsDQCAGKAIUIAtBKGxqIggoAhQiAiAIKAIQIgdsBEBBACEEA0AgCCgCGCAEQQZ0aiIDKAI8IhEEQCADKAIMIQcgAygCFCESIAMoAhAhDCADKAIIIhMgBigCAGshAyAGKAIQIg1BAXEEQCAAKAIcIA9qIgJBkAFrKAIAIANqIAJBmAFrKAIAayEDCyAHIAYoAgRrIQIgDUECcQRAIAIgACgCHCAPaiINQYwBaygCAGogDUGUAWsoAgBrIQILIAkgAyACIAMgDCATayIMaiASIAdrIAJqIBFBASAMQQAQJkUNCSAIKAIQIQcgCCgCFCECCyAEQQFqIgQgAiAHbEkNAAsgBSgCECEEIAUoAhQhAwsgC0EBaiILIAMgBGxJDQALIAUoAhghAgsgCkEBaiIKIAJJDQALCyAOQQFqIg4gAUcNAAsLIAkPCyAJECNBAAvQDAIQfwZ7IAAoAggiCyAAKAIEaiEHAkAgACgCDEUEQCAHQQJIDQEgASgCACABIAtBAnRqIg0oAgAiBEEBakEBdWshAyAAKAIAIQYCQCAHQQRJBEAgBCECDAELIAdBBGsiAEEBdiIJQQFqIQwCQCAAQRZJBEBBASEADAELIAYgASALQQJ0aiIFIAlBAnQiAmpBCGpJIAYgCUEDdGpBCGoiACAFQQRqS3EEQEEBIQAMAQsgBiABIAJqQQhqSSABQQRqIABJcQRAQQEhAAwBCyAMQfz///8HcSIFQQFyIQAgBUEBdCEIIAT9ESESIAP9ESET/QwAAAAAAgAAAAQAAAAGAAAAIRZBACECA0AgASACQQJ0QQRyIgNq/QACACEVIAMgDWr9AAIAIRQgBiACQQN0aiIDIBP9WgIAAyADQQhqIBUgFCASIBT9DQwNDg8QERITFBUWFxgZGhsiFf2uAf0MAgAAAAIAAAACAAAAAgAAAP2uAUEC/awB/bEBIhL9WgIAACADQRBqIBL9WgIAASADQRhqIBL9WgIAAiAGIBb9DAEAAAABAAAAAQAAAAEAAAD9UCIX/RsAQQJ0aiASIBMgEv0NDA0ODxAREhMUFRYXGBkaG/2uAUEB/awBIBX9rgEiE/1aAgAAIAYgF/0bAUECdGogE/1aAgABIAYgF/0bAkECdGogE/1aAgACIAYgF/0bA0ECdGogE/1aAgADIBb9DAgAAAAIAAAACAAAAAgAAAD9rgEhFiASIRMgFCESIAJBBGoiAiAFRw0ACyAS/RsDIQIgE/0bAyEDIAUgDEYNASACIQQLA0AgASAAQQJ0IgJqKAIAIQkgAiANaigCACECIAYgCEECdGoiBSADNgIAIAUgAyAJIAIgBGpBAmpBAnVrIgNqQQF1IARqNgIEIAhBAmohCCAAIAxHIRAgAiEEIABBAWohACAQDQALCyAGIAhBAnRqIAM2AgBBfCEAIAdBAXEEfyAGIAdBAWsiAEECdGogASAAQQF0aigCACACQQFqQQF1ayIANgIAIAAgA2pBAXUhA0F4BUF8CyAGIAdBAnQiAGpqIAIgA2o2AgAgASAGIAAQEhoPCwJAAkACQCAHQQFrDgIAAQILIAEgASgCAEECbTYCAA8LIAAoAgAiBCABKAIAIAEgC0ECdGoiAygCAEEBakEBdWsiADYCBCAEIAAgAygCAGo2AgAgASAEKQIANwIADwsgB0EDSA0AIAAoAgAiCiABKAIAIAEgC0ECdGoiDigCBCIEIA4oAgAiAGpBAmpBAnVrIgMgAGo2AgBBASEIAkAgB0ECayIGIAdBAXEiDEUiAGtBAkkEQCAEIQIMAQsgByAAa0EEayIAQQF2IgJBAWohDwJAAkAgAEEWSQ0AIApBBGoiBSABIAJBAnQiAGpBCGpJIAogAkEDdGpBDGoiAiABQQRqS3ENACAFIAAgASALQQJ0aiIAakEMakkgAEEIaiACSXENACAPQXxxIgVBAXIhACAFQQF0QQFyIQggBP0RIRMgA/0RIRJBACECA0AgCiACQQN0aiIEIAEgAkECdCIDav0AAgQgEyADIA5q/QACCCIT/Q0MDQ4PEBESExQVFhcYGRobIhUgE/2uAf0MAgAAAAIAAAACAAAAAgAAAP2uAUEC/awB/bEBIhQgFCASIBT9DQwNDg8QERITFBUWFxgZGhv9rgFBAf2sASAV/a4BIhX9DQQFBgcYGRobCAkKCxwdHh/9CwIUIAQgEiAV/Q0MDQ4PEBESEwABAgMUFRYXIBT9DQABAgMEBQYHEBESEwwNDg/9CwIEIBQhEiACQQRqIgIgBUcNAAsgE/0bAyECIBL9GwMhAyAFIA9GDQIgAiEEDAELQQEhAAsDQCABIABBAnRqKAIAIQ0gDiAAQQFqIgVBAnRqKAIAIQIgCiAIQQJ0aiIJIAM2AgAgCSADIA0gAiAEakECakECdWsiA2pBAXUgBGo2AgQgCEECaiEIIAAgD0chESACIQQgBSEAIBENAAsLIAogCEECdGogAzYCAAJAIAxFBEAgCiAGQQJ0aiABIAdBAXRqQQRrKAIAIAJBAWpBAXVrIgAgA2pBAXUgAmo2AgAMAQsgAiADaiEACyAKIAdBAnQiA2pBBGsgADYCACABIAogAxASGgsLoAcDA30DewJ/IANBCE8EQCADQQN2IQsDQCAB/QAEACEHIAAgAP0ABAAiCCAC/QAEACIJ/Qy8dLM/vHSzP7x0sz+8dLM//eYB/eQB/QsEACABIAggB/0MzzGwPs8xsD7PMbA+zzGwPv3mAf3lASAJ/Qzh0TY/4dE2P+HRNj/h0TY//eYB/eUB/QsEACACIAggB/0M5dDiP+XQ4j/l0OI/5dDiP/3mAf3kAf0LBAAgAf0ABBAhByAAIAD9AAQQIgggAv0ABBAiCf0MvHSzP7x0sz+8dLM/vHSzP/3mAf3kAf0LBBAgASAIIAf9DM8xsD7PMbA+zzGwPs8xsD795gH95QEgCf0M4dE2P+HRNj/h0TY/4dE2P/3mAf3lAf0LBBAgAiAIIAf9DOXQ4j/l0OI/5dDiP+XQ4j/95gH95AH9CwQQIAJBIGohAiABQSBqIQEgAEEgaiEAIApBAWoiCiALRw0ACwsCQCADQQdxIgNFDQAgASoCACEEIAAgAioCACIGQ7x0sz+UIAAqAgAiBZI4AgAgASAFIARDzzGwvpSSIAZD4dE2v5SSOAIAIAIgBSAEQ+XQ4j+UkjgCACADQQFGDQAgASoCBCEEIAAgAioCBCIGQ7x0sz+UIAAqAgQiBZI4AgQgASAFIARDzzGwvpSSIAZD4dE2v5SSOAIEIAIgBSAEQ+XQ4j+UkjgCBCADQQJGDQAgASoCCCEEIAAgAioCCCIGQ7x0sz+UIAAqAggiBZI4AgggASAFIARDzzGwvpSSIAZD4dE2v5SSOAIIIAIgBSAEQ+XQ4j+UkjgCCCADQQNGDQAgASoCDCEEIAAgAioCDCIGQ7x0sz+UIAAqAgwiBZI4AgwgASAFIARDzzGwvpSSIAZD4dE2v5SSOAIMIAIgBSAEQ+XQ4j+UkjgCDCADQQRGDQAgASoCECEEIAAgAioCECIGQ7x0sz+UIAAqAhAiBZI4AhAgASAFIARDzzGwvpSSIAZD4dE2v5SSOAIQIAIgBSAEQ+XQ4j+UkjgCECADQQVGDQAgASoCFCEEIAAgAioCFCIGQ7x0sz+UIAAqAhQiBZI4AhQgASAFIARDzzGwvpSSIAZD4dE2v5SSOAIUIAIgBSAEQ+XQ4j+UkjgCFCADQQZGDQAgASoCGCEEIAAgAioCGCIGQ7x0sz+UIAAqAhgiBZI4AhggASAFIARDzzGwvpSSIAZD4dE2v5SSOAIYIAIgBSAEQ+XQ4j+UkjgCGAsL4AECBn8DewJAIANFDQAgA0EETwRAIANBfHEhBgNAIAAgBEECdCIFaiIHIAf9AAIAIAIgBWoiB/0AAgAiCyABIAVqIgX9AAIAIgz9rgFBAv2sAf2xASIKIAv9rgH9CwIAIAUgCv0LAgAgByAKIAz9rgH9CwIAIARBBGoiBCAGRw0ACyADIAZGDQELA0AgACAGQQJ0IgRqIgUgBSgCACACIARqIgUoAgAiByABIARqIggoAgAiCWpBAnVrIgQgB2o2AgAgCCAENgIAIAUgBCAJajYCACAGQQFqIgYgA0cNAAsLC90BAQR/IwBBgAFrIgYkACAGIQUCQCABKAIMIAJBBHRqIgIoAgAiBEUEQCACIQEMAQsDQCAFIAI2AgAgBUEEaiEFIAQiASICKAIAIgQNAAsLQQAhBANAIAEoAggiAiAESARAIAEgBDYCCCAEIQILAkAgAiADTg0AA0AgAiABKAIETg0BAkAgAEEBEB8EQCABIAI2AgQMAQsgAkEBaiECCyACIANIDQALCyABIAI2AgggBSAGRwRAIAVBBGsiBSgCACEBIAIhBAwBCwsgASgCBCEHIAZBgAFqJAAgByADSAv9BgELfyMAQYACayIKJAACQCAARQRAQQAhAAwBCwJAIAEgACgCAEYEQCAAKAIEIAJGDQELIAAgAjYCBCAAIAE2AgAgCiACNgIAIAogATYCgAEgAiEEIAEhBQNAIAogByIMQQFqIgdBAnQiCGogBEEBakECbSIJNgIAIApBgAFqIAhqIAVBAWpBAm0iCDYCACAGIAQgBWwiC2ohBiAJIQQgCCEFIAtBAUsNAAsgACAGNgIIAkACQAJAAkAgBkUEQCAAKAIMIgRFDQIgAEEMaiEFDAELIAZBBHQiBCAAKAIQTQ0DIAAoAgwgBBAXIgENAiADQQFBmjFBABAPIABBDGoiBSgCACIERQ0BCyAEEBAgBUEANgIACyAAEBBBACEADAMLIAAgATYCDCABIAAoAhAiAmpBACAEIAJrEBUaIAAgBDYCECAAKAIEIQIgACgCACEBCyAAKAIMIQUgDARAQQAhAyAFIAEgAmxBBHRqIgQhBgNAAkAgCiADQQJ0IgFqKAIAIghBAEwNACAIQQFrIQtBACEJAkACQCAKQYABaiABaigCACICQQBMBEAgCEEBcSENQQAhByAIQQFHDQEgBiEBDAILA0AgBiEBIAIhBgNAAkAgBSAENgIAIAZBAUYEQCAFQRBqIQUgBEEQaiEEDAELIAUgBDYCECAEQRBqIQQgBUEgaiEFIAZBAkohDiAGQQJrIQYgDg0BCwsgBCABIAJBBHRqIAkgCSALRnJBAXEiBxshBiAEIAEgBxshBCAJQQFqIgkgCEcNAAsMAgsgCEH+////B3EhCANAIAcgC0YhASAHQQJqIQcgBCAGIAEbIgQhBiAEIQEgCUECaiIJIAhHDQALCyANRQRAIAQhBgwBCyAEIAEgAkEEdGogByAHIAtGckEBcSICGyEGIAQgASACGyEECyADQQFqIgMgDEcNAAsLIAVBADYCAAsgACgCCCIBRQ0AIAAoAgwhBCABQQRPBEAgAUF8cSECQQAhBQNAIARBADYCPCAEQucHNwI0IARBADYCLCAEQucHNwIkIARBADYCHCAEQucHNwIUIARBADYCDCAEQucHNwIEIARBQGshBCAFQQRqIgUgAkcNAAsLIAFBA3EiAUUNAEEAIQUDQCAEQQA2AgwgBELnBzcCBCAEQRBqIQQgBUEBaiIFIAFHDQALCyAKQYACaiQAIAALsQEBA38CQCAARQ0AIAAoAggiAUUNACAAKAIMIQAgAUEETwRAIAFBfHEhAwNAIABBADYCPCAAQucHNwI0IABBADYCLCAAQucHNwIkIABBADYCHCAAQucHNwIUIABBADYCDCAAQucHNwIEIABBQGshACACQQRqIgIgA0cNAAsLIAFBA3EiAUUNAEEAIQIDQCAAQQA2AgwgAELnBzcCBCAAQRBqIQAgAkEBaiICIAFHDQALCwv7BQEQfyMAQYACayIIJAACf0EBQRQQEyIGRQRAIAJBAUH0MEEAEA9BAAwBCyAGIAE2AgQgBiAANgIAIAggATYCACAIIAA2AoABA0AgCCAFIg1BAWoiBUECdCIHaiABQQFqQQJtIgM2AgAgCEGAAWogB2ogAEEBakECbSIHNgIAIAQgACABbCIJaiEEIAMhASAHIQAgCUEBSw0ACyAGIAQ2AgggBEUEQCAGEBBBAAwBCyAGIARBEBATIgM2AgwgA0UEQCACQQFB2hpBABAPIAYQEEEADAELIAYgBigCCCILQQR0NgIQIAMhACANBEAgAyAGKAIEIAYoAgBsQQR0aiIEIQEDQAJAIAggDkECdCICaigCACIJQQBMDQAgCUEBayEMQQAhBwJAIAhBgAFqIAJqKAIAIgJBAEwEQEEAIQUgCUEBRwRAIAlB/v///wdxIQoDQCAFIAxGIQ8gBUECaiEFIAEgBCAPGyIEIQEgB0ECaiIHIApHDQALCyAJQQFxDQEgBCEBDAILA0AgBCEFIAIhBANAAkAgACABNgIAIARBAUYEQCAAQRBqIQAgAUEQaiEBDAELIAAgATYCECABQRBqIQEgAEEgaiEAIARBAkohECAEQQJrIQQgEA0BCwsgASAFIAJBBHRqIAcgByAMRnJBAXEiChshBCABIAUgChshASAHQQFqIgcgCUcNAAsMAQsgASAEIAJBBHRqIAUgBSAMRnJBAXEiBRshESABIAQgBRshASARIQQLIA5BAWoiDiANRw0ACwsgAEEANgIAAkAgC0UNACALQQRPBEAgC0F8cSEAQQAhAQNAIANBADYCPCADQucHNwI0IANBADYCLCADQucHNwIkIANBADYCHCADQucHNwIUIANBADYCDCADQucHNwIEIANBQGshAyABQQRqIgEgAEcNAAsLIAtBA3EiAEUNAEEAIQEDQCADQQA2AgwgA0LnBzcCBCADQRBqIQMgAUEBaiIBIABHDQALCyAGCyESIAhBgAJqJAAgEgtTAQF/An8gAC0ADEH/AUYEQCAAQoD+g4DwADcCDEEAIAAoAggiASAAKAIETw0BGiAAIAFBAWo2AgggACABLQAAQYD+A3I2AgwLIABBADYCEEEBCwt+AgF/AX4gAL0iA0I0iKdB/w9xIgJB/w9HBHwgAkUEQCABIABEAAAAAAAAAABhBH9BAAUgAEQAAAAAAADwQ6IgARBlIQAgASgCAEFAags2AgAgAA8LIAEgAkH+B2s2AgAgA0L/////////h4B/g0KAgICAgICA8D+EvwUgAAsLSQEBfwJAQQFBLBATIgEEQCABQQA2AhACQCAAQQBMBEAgAUEBQQgQEyIANgIkIABFDQEMAwsgAUEANgIMCyABEBALQQAhAQsgAQuRAgAgAEUEQEEADwsCfwJAIAFB/wBNDQACQEGU0AEoAgAoAgBFBEAgAUGAf3FBgL8DRg0CDAELIAFB/w9NBEAgACABQT9xQYABcjoAASAAIAFBBnZBwAFyOgAAQQIMAwsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMMAwsgAUGAgARrQf//P00EQCAAIAFBP3FBgAFyOgADIAAgAUESdkHwAXI6AAAgACABQQZ2QT9xQYABcjoAAiAAIAFBDHZBP3FBgAFyOgABQQQMAwsLQZTHAUEZNgIAQX8MAQsgACABOgAAQQELC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAwALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC3MBBn8gACgCACIDLAAAQTBrIgFBCUsE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AyAAQRh2NgIUIAMgBDYCECADIABBCHZB/wFxNgIcIAMgAEEQdkH/AXE2AhggAkEBQc31ACADQRBqEA8MBwsgBSAITQRAIAYhBAwECyAFIQggBiAFEBciBA0DIAYQECACQQFB/w9BABAPQQAMBwsgBEECcUUEQCACQQFBwg9BABAPIAYQEEEADAcLIAAgBEH/////B3I2AmQgASAHIAVrrSINIAIgASgCKBEIACANUQ0DIAAtAGRBCHFFDQEgAkECQZIcQQAQDwsgBhAQQQEMBQsgAkEBQZIcQQAQDyAGEBBBAAwECyABIAQgBSACEBogBUcEQCACQQFBxBxBABAPIAQQEEEADAQLIAAgBCIGIAUgAiAKKAIEEQEADQALIAQQEEEADAILIAJBAUGiJUEAEA9BAAwBCyAGEBBBAAshDCADQfAAaiQAIAwL5gEBBn8gACgCCEE1IAIQJARAAkAgACgCCCIGKAIAIQMgBigCCCEFAkACQAJ/AkAgAwRAQQEhBCADQQFxIQcgA0EBRw0BQQAMAgsgBkEANgIADAILIANBfnEhAwNAAn9BACAERQ0AGkEAIAAgASACIAUoAgARAABFDQAaIAAgASACIAUoAgQRAABBAEcLIQQgBUEIaiEFIAhBAmoiCCADRw0ACyAERQshAyAHBEAgAw0CIAAgASACIAUoAgARAABBAEchBAsgBkEANgIAIARFDQILIAAoAgAaQQEPCyAGQQA2AgALC0EACwoAIAAoAgAaQQALFAAgACgCACIABEAgACABNgK4AQsLIQAgACgCACABEFMgAEEAOgB8IAAgASgCuEBBAXE2AoABCzIAIAJFBEBBAA8LIAAoAgAgASACIAMQSEUEQCADQQFBqS9BABAPQQAPCyAAIAIgAxBxC2kCAn8BfCMAQRBrIgMkACACBEADQCAAIANBCGoQRCABAn8gAysDCCIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAs2AgAgAUEEaiEBIABBCGohACAEQQFqIgQgAkcNAAsLIANBEGokAAuEAQICfwF9IwBBEGsiAyQAIAIEQANAIAMgAC0AADoADyADIAAtAAE6AA4gAyAALQACOgANIAMgAC0AAzoADCABAn8gAyoCDCIFi0MAAABPXQRAIAWoDAELQYCAgIB4CzYCACABQQRqIQEgAEEEaiEAIARBAWoiBCACRw0ACwsgA0EQaiQAC0sBAn8jAEEQayIDJAAgAgRAA0AgACADQQxqQQQQESABIAMoAgw2AgAgAUEEaiEBIABBBGohACAEQQFqIgQgAkcNAAsLIANBEGokAAtLAQJ/IwBBEGsiAyQAIAIEQANAIAAgA0EMakECEBEgASADKAIMNgIAIAFBBGohASAAQQJqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALSgECfyMAQRBrIgMkACACBEADQCAAIANBCGoQRCABIAMrAwi2OAIAIAFBBGohASAAQQhqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALaAECfyMAQRBrIgMkACACBEADQCADIAAtAAA6AA8gAyAALQABOgAOIAMgAC0AAjoADSADIAAtAAM6AAwgASADKgIMOAIAIAFBBGohASAAQQRqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALTAECfyMAQRBrIgMkACACBEADQCAAIANBDGpBBBARIAEgAygCDLM4AgAgAUEEaiEBIABBBGohACAEQQFqIgQgAkcNAAsLIANBEGokAAtMAQJ/IwBBEGsiAyQAIAIEQANAIAAgA0EMakECEBEgASADKAIMszgCACABQQRqIQEgAEECaiEAIARBAWoiBCACRw0ACwsgA0EQaiQAC6oIAg1/AXsjAEEQayIIJAACfyAAKAIIQRBGBEAgACgCnAEgACgCzAFBjCxsagwBCyAAKAIMCyEJAkAgAkUEQCADQQFB8B9BABAPDAELIAAoAkghBkEBIQQgASAIQQhqQQEQESAIKAIIIgVBAk8EQCADQQJBxsgAQQAQDwwBCyACIAVBAWpHBEBBACEEIANBAkHwH0EAEA8MAQsCQCAGKAIQIgNFDQAgCSgC0CshBCADQQhPBEAgA0F4cSEGQQAhAgNAIARBADYCvEMgBEEANgKEOyAEQQA2AswyIARBADYClCogBEEANgLcISAEQQA2AqQZIARBADYC7BAgBEEANgK0CCAEQcDDAGohBCACQQhqIgIgBkcNAAsLIANBB3EiA0UNAEEAIQIDQCAEQQA2ArQIIARBuAhqIQQgAkEBaiICIANHDQALCyAJKALoKyICBH8gAhAQIAlBADYC6CsgCCgCCAUgBQtFBEBBASEEDAELA0AgAUEBaiIBIAhBDGpBARARAkAgCSgCgCxF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KT0ALgAobnVsbCkAKCVkLCVkKSAAJXN9CgAJCSB9CgBbREVWXSBEdW1wIGFuIGltYWdlX2NvbXBfaGVhZGVyIHN0cnVjdCB7CgBbREVWXSBEdW1wIGFuIGltYWdlX2hlYWRlciBzdHJ1Y3QgewoASW1hZ2UgaW5mbyB7CgAJIGRlZmF1bHQgdGlsZSB7CgAlcwkgY29tcG9uZW50ICVkIHsKAAkJIGNvbXAgJWQgewoACSBUaWxlIGluZGV4OiB7CgAJIE1hcmtlciBsaXN0OiB7CgBDb2Rlc3RyZWFtIGluZGV4IGZyb20gbWFpbiBoZWFkZXI6IHsKAENvZGVzdHJlYW0gaW5mbyBmcm9tIG1haW4gaGVhZGVyOiB7CgBTdHJlYW0gZXJyb3Igd2hpbGUgcmVhZGluZyBKUDIgSGVhZGVyIGJveAoARm91bmQgYSBtaXNwbGFjZWQgJyVjJWMlYyVjJyBib3ggb3V0c2lkZSBqcDJoIGJveAoATWFsZm9ybWVkIEpQMiBmaWxlIGZvcm1hdDogZmlyc3QgYm94IG11c3QgYmUgSlBFRyAyMDAwIHNpZ25hdHVyZSBib3gKAE1hbGZvcm1lZCBKUDIgZmlsZSBmb3JtYXQ6IHNlY29uZCBib3ggbXVzdCBiZSBmaWxlIHR5cGUgYm94CgBOb3QgZW5vdWdoIG1lbW9yeSB0byBoYW5kbGUganBlZzIwMDAgYm94CgBOb3QgZW5vdWdoIG1lbW9yeSB3aXRoIEZUWVAgQm94CgBBIG1hcmtlciBJRCB3YXMgZXhwZWN0ZWQgKDB4ZmYtLSkgaW5zdGVhZCBvZiAlLjh4CgAJCSBtY3Q9JXgKAAkJCSBjYmxrc3R5PSUjeAoACQkJIGNzdHk9JSN4CgAJCSBwcmc9JSN4CgBJbnRlZ2VyIG92ZXJmbG93CgAJIHRkeD0ldSwgdGR5PSV1CgAJIHR3PSV1LCB0aD0ldQoACSB0eDA9JXUsIHR5MD0ldQoASW52YWxpZCBjb21wb25lbnQgaW5kZXg6ICV1CgBTdHJlYW0gdG9vIHNob3J0CgBNYXJrZXIgaGFuZGxlciBmdW5jdGlvbiBmYWlsZWQgdG8gcmVhZCB0aGUgbWFya2VyIHNlZ21lbnQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjdXJyZW50IHByZWNpbmN0IGNvZGVibG9jayBlbGVtZW50CgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQKAEVycm9yIHJlYWRpbmcgU1FjZCBvciBTUWNjIGVsZW1lbnQKAEEgQlBDQyBoZWFkZXIgYm94IGlzIGF2YWlsYWJsZSBhbHRob3VnaCBCUEMgZ2l2ZW4gYnkgdGhlIElIRFIgYm94ICglZCkgaW5kaWNhdGUgY29tcG9uZW50cyBiaXQgZGVwdGggaXMgY29uc3RhbnQKAEVycm9yIHdpdGggU0laIG1hcmtlcjogaWxsZWdhbCB0aWxlIG9mZnNldAoASW52YWxpZCBwcmVjaW5jdAoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGJhbmQgcHJlY2ludHMKAEZhaWxlZCB0byBkZWNvZGUgYWxsIHVzZWQgY29tcG9uZW50cwoAU2l6ZSBvZiBjb2RlIGJsb2NrIGRhdGEgZXhjZWVkcyBzeXN0ZW0gbGltaXRzCgBTaXplIG9mIHRpbGUgZGF0YSBleGNlZWRzIHN5c3RlbSBsaW1pdHMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtdWx0aXBsZSBNQ1QgbWFya2VycwoAQ29ycnVwdGVkIFBQTSBtYXJrZXJzCgBOb3QgZW5vdWdoIG1lbW9yeSBmb3IgdGlsZSByZXNvbHV0aW9ucwoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIGNvbGxlY3Rpb25zCgBJbnZhbGlkIFBDTFIgYm94LiBSZXBvcnRzIDAgcGFsZXR0ZSBjb2x1bW5zCgBXZSBkbyBub3Qgc3VwcG9ydCBST0kgaW4gZGVjb2RpbmcgSFQgY29kZWJsb2NrcwoAQ2Fubm90IGhhbmRsZSBib3ggb2YgdW5kZWZpbmVkIHNpemVzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgd2l0aG91dCBzYW1lIG51bWJlciBvZiBpbmRpeGVzCgBJbnZhbGlkIHRpbGVjLT53aW5feHh4IHZhbHVlcwoAQ2Fubm90IGhhbmRsZSBib3ggb2YgbGVzcyB0aGFuIDggYnl0ZXMKAENhbm5vdCBoYW5kbGUgWEwgYm94IG9mIGxlc3MgdGhhbiAxNiBieXRlcwoAQ29tcG9uZW50IGluZGV4ICV1IHVzZWQgc2V2ZXJhbCB0aW1lcwoASW52YWxpZCBQQ0xSIGJveC4gUmVwb3J0cyAlZCBlbnRyaWVzCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBjcmVhdGUgVGFnLXRyZWUgbm9kZXMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtY3QgZGF0YSB3aXRoaW4gbXVsdGlwbGUgTUNUIHJlY29yZHMKAENhbm5vdCBkZWNvZGUgdGlsZSwgbWVtb3J5IGVycm9yCgBvcGpfajJrX2FwcGx5X25iX3RpbGVfcGFydHNfY29ycmVjdGlvbiBlcnJvcgoAUHJvYmxlbSB3aXRoIHNraXBwaW5nIEpQRUcyMDAwIGJveCwgc3RyZWFtIGVycm9yCgBQcm9ibGVtIHdpdGggcmVhZGluZyBKUEVHMjAwMCBib3gsIHN0cmVhbSBlcnJvcgoAVW5rbm93biBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCB0bCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCBtaCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHRha2UgaW4gY2hhcmdlIFNJWiBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUFBUIG1hcmtlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBQUFQgbWFya2VyCgBFcnJvciByZWFkaW5nIFNPVCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUExUIG1hcmtlcgoARXJyb3IgcmVhZGluZyBNQ1QgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIE1DVCBtYXJrZXIKAE5vdCBlbm91Z2ggc3BhY2UgZm9yIGV4cGVjdGVkIFNPUCBtYXJrZXIKAEV4cGVjdGVkIFNPUCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgTUNPIG1hcmtlcgoARXJyb3IgcmVhZGluZyBSR04gbWFya2VyCgBFcnJvciByZWFkaW5nIFBQTSBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgUFBNIG1hcmtlcgoARXJyb3IgcmVhZGluZyBUTE0gbWFya2VyCgBFcnJvciByZWFkaW5nIFBMTSBtYXJrZXIKAE5vdCBlbm91Z2ggc3BhY2UgZm9yIGV4cGVjdGVkIEVQSCBtYXJrZXIKAEV4cGVjdGVkIEVQSCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgQ1JHIG1hcmtlcgoAVW5rbm93biBwcm9ncmVzc2lvbiBvcmRlciBpbiBDT0QgbWFya2VyCgBVbmtub3duIFNjb2QgdmFsdWUgaW4gQ09EIG1hcmtlcgoARXJyb3IgcmVhZGluZyBDT0QgbWFya2VyCgBFcnJvciByZWFkaW5nIFFDRCBtYXJrZXIKAENycm9yIHJlYWRpbmcgQ0JEIG1hcmtlcgoARXJyb3IgcmVhZGluZyBQT0MgbWFya2VyCgBFcnJvciByZWFkaW5nIENPQyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUUNDIG1hcmtlcgoARXJyb3IgcmVhZGluZyBNQ0MgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIE1DQyBtYXJrZXIKAHJlcXVpcmVkIFNJWiBtYXJrZXIgbm90IGZvdW5kIGluIG1haW4gaGVhZGVyCgByZXF1aXJlZCBDT0QgbWFya2VyIG5vdCBmb3VuZCBpbiBtYWluIGhlYWRlcgoAcmVxdWlyZWQgUUNEIG1hcmtlciBub3QgZm91bmQgaW4gbWFpbiBoZWFkZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGhhbmRsZSBqcGVnMjAwMCBmaWxlIGhlYWRlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBoZWFkZXIKAEVycm9yIHdpdGggSlAgU2lnbmF0dXJlIDogYmFkIG1hZ2ljIG51bWJlcgoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgY3VycmVudCBudW1iZXIgb2YgdGlsZS1wYXJ0ICglZCksIGdpdmluZyB1cAoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgcHJldmlvdXMgbnVtYmVyIG9mIHRpbGUtcGFydCAoJWQpLCBnaXZpbmcgdXAKAEluIFNPVCBtYXJrZXIsIFRQU290ICglZCkgaXMgbm90IHZhbGlkIHJlZ2FyZHMgdG8gdGhlIGN1cnJlbnQgbnVtYmVyIG9mIHRpbGUtcGFydCAoaGVhZGVyKSAoJWQpLCBnaXZpbmcgdXAKAHRpbGVzIHJlcXVpcmUgYXQgbGVhc3Qgb25lIHJlc29sdXRpb24KAE1hcmtlciBpcyBub3QgY29tcGxpYW50IHdpdGggaXRzIHBvc2l0aW9uCgBQcm9ibGVtIHdpdGggc2VlayBmdW5jdGlvbgoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50LCBJbnZhbGlkIGNibGt3L2NibGtoIGNvbWJpbmF0aW9uCgBJbnZhbGlkIG11bHRpcGxlIGNvbXBvbmVudCB0cmFuc2Zvcm1hdGlvbgoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIGNvbGxlY3Rpb25zIG90aGVyIHRoYW4gYXJyYXkgZGVjb3JyZWxhdGlvbgoAVG9vIGxhcmdlIHZhbHVlIGZvciBOcHBtCgBOb3QgZW5vdWdoIGJ5dGVzIHRvIHJlYWQgTnBwbQoAYmFkIHBsYWNlZCBqcGVnIGNvZGVzdHJlYW0KAAkgTWFpbiBoZWFkZXIgc3RhcnQgcG9zaXRpb249JWxsaQoJIE1haW4gaGVhZGVyIGVuZCBwb3NpdGlvbj0lbGxpCgBNYXJrZXIgc2l6ZSBpbmNvbnNpc3RlbnQgd2l0aCBzdHJlYW0gbGVuZ3RoCgBUaWxlIHBhcnQgbGVuZ3RoIHNpemUgaW5jb25zaXN0ZW50IHdpdGggc3RyZWFtIGxlbmd0aAoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIGRhdGEgc3Bhbm5pbmcKAFdyb25nIGZsYWcKAEVycm9yIHdpdGggRlRZUCBzaWduYXR1cmUgQm94IHNpemUKAEVycm9yIHdpdGggSlAgc2lnbmF0dXJlIEJveCBzaXplCgBJbnZhbGlkIHByZWNpbmN0IHNpemUKAEluY29uc2lzdGVudCBtYXJrZXIgc2l6ZQoASW52YWxpZCBtYXJrZXIgc2l6ZQoARXJyb3Igd2l0aCBTSVogbWFya2VyIHNpemUKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCBhIG5ldyB2YWxpZGF0aW9uIHByb2NlZHVyZQoATm90IGVub3VnaCBtZW1vcnkgdG8gZGVjb2RlIHRpbGUKAEZhaWxlZCB0byBkZWNvZGUgdGhlIGNvZGVzdHJlYW0gaW4gdGhlIEpQMiBmaWxlCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgd2l0aCBpbmRpeCBzaHVmZmxlCgBDYW5ub3QgYWxsb2NhdGUgVGllciAxIGhhbmRsZQoATm8gZGVjb2RlZCBhcmVhIHBhcmFtZXRlcnMsIHNldCB0aGUgZGVjb2RlZCBhcmVhIHRvIHRoZSB3aG9sZSBpbWFnZQoATm90IGVub3VnaCBtZW1vcnkgdG8gY3JlYXRlIFRhZy10cmVlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWluaXRpYWxpemUgdGhlIHRhZyB0cmVlCgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQsIEludmFsaWQgdHJhbnNmb3JtYXRpb24gZm91bmQKAEVycm9yIHJlYWRpbmcgU1BDb2QgU1BDb2MgZWxlbWVudC4gVW5zdXBwb3J0ZWQgTWl4ZWQgSFQgY29kZS1ibG9jayBzdHlsZSBmb3VuZAoAVGlsZSBZIGNvb3JkaW5hdGVzIGFyZSBub3Qgc3VwcG9ydGVkCgBUaWxlIFggY29vcmRpbmF0ZXMgYXJlIG5vdCBzdXBwb3J0ZWQKAEltYWdlIGNvb3JkaW5hdGVzIGFib3ZlIElOVF9NQVggYXJlIG5vdCBzdXBwb3J0ZWQKAEpQRUcyMDAwIEhlYWRlciBib3ggbm90IHJlYWQgeWV0LCAnJWMlYyVjJWMnIGJveCB3aWxsIGJlIGlnbm9yZWQKAG9wal9qMmtfbWVyZ2VfcHB0KCkgaGFzIGFscmVhZHkgYmVlbiBjYWxsZWQKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgU09UIG1hcmtlci4gVGlsZSBpbmRleCBhbGxvY2F0aW9uIGZhaWxlZAoASWdub3JpbmcgaWhkciBib3guIEZpcnN0IGloZHIgYm94IGFscmVhZHkgcmVhZAoAWnBwdCAldSBhbHJlYWR5IHJlYWQKAFpwcG0gJXUgYWxyZWFkeSByZWFkCgBQVEVSTSBjaGVjayBmYWlsdXJlOiAlZCBzeW50aGV0aXplZCAweEZGIG1hcmtlcnMgcmVhZAoACQkJIGNibGt3PTJeJWQKAAkJCSBjYmxraD0yXiVkCgAJCQkgcW50c3R5PSVkCgAlcyBkeD0lZCwgZHk9JWQKAAkJCSByb2lzaGlmdD0lZAoACQkJIG51bWdiaXRzPSVkCgAJCSBudW1sYXllcnM9JWQKACVzIG51bWNvbXBzPSVkCgBvcGpfanAyX2FwcGx5X2NkZWY6IGFjbj0lZCwgbnVtY29tcHM9JWQKAG9wal9qcDJfYXBwbHlfY2RlZjogY249JWQsIG51bWNvbXBzPSVkCgAJCQkgbnVtcmVzb2x1dGlvbnM9JWQKAAkJIHR5cGU9JSN4LCBwb3M9JWxsaSwgbGVuPSVkCgAlcyBzZ25kPSVkCgAJCQkgcW1mYmlkPSVkCgAlcyBwcmVjPSVkCgAJCSBuYiBvZiB0aWxlLXBhcnQgaW4gdGlsZSBbJWRdPSVkCgAlcyB4MT0lZCwgeTE9JWQKACVzIHgwPSVkLCB5MD0lZAoARmFpbGVkIHRvIGRlY29kZSB0aWxlICVkLyVkCgBTZXR0aW5nIGRlY29kaW5nIGFyZWEgdG8gJWQsJWQsJWQsJWQKAEZhaWxlZCB0byBkZWNvZGUgY29tcG9uZW50ICVkCgBJbnZhbGlkIHZhbHVlIGZvciBudW1yZXNvbHV0aW9ucyA6ICVkLCBtYXggdmFsdWUgaXMgc2V0IGluIG9wZW5qcGVnLmggYXQgJWQKAEludmFsaWQgY29tcG9uZW50IG51bWJlcjogJWQsIHJlZ2FyZGluZyB0aGUgbnVtYmVyIG9mIGNvbXBvbmVudHMgJWQKAFRvbyBtYW55IFBPQ3MgJWQKAEludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBwYXJ0IGluZGV4IGZvciB0aWxlIG51bWJlciAlZC4gR290ICVkLCBleHBlY3RlZCAlZAoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBudW1iZXIgb2YgY29tcG9uZW50IGlzIGlsbGVnYWwgLT4gJWQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjaWVsYWIKAENhbm5vdCBhbGxvY2F0ZSBjYmxrLT5kZWNvZGVkX2RhdGEKAEZhaWxlZCB0byBtZXJnZSBQUFQgZGF0YQoARmFpbGVkIHRvIG1lcmdlIFBQTSBkYXRhCgBJbnZhbGlkIG51bWJlciBvZiBsYXllcnMgaW4gQ09EIG1hcmtlciA6ICVkIG5vdCBpbiByYW5nZSBbMS02NTUzNV0KACVzOiVkOmNvbG9yX2NteWtfdG9fcmdiCglDQU4gTk9UIENPTlZFUlQKACVzOiVkOmNvbG9yX2VzeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgAlczolZDpjb2xvcl9zeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgBTdHJlYW0gdG9vIHNob3J0LCBleHBlY3RlZCBTT1QKAFVuYWJsZSB0byBzZXQgdDEgaGFuZGxlIGFzIFRMUwoAU3RyZWFtIGRvZXMgbm90IGVuZCB3aXRoIEVPQwoAQ2Fubm90IGhhbmRsZSBib3ggc2l6ZXMgaGlnaGVyIHRoYW4gMl4zMgoAb3BqX3BpX25leHRfbHJjcCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcmxjcCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfY3BybCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcGNybCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcnBjbCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3QxX2RlY29kZV9jYmxrKCk6IHVuc3VwcG9ydGVkIGJwbm9fcGx1c19vbmUgPSAlZCA+PSAzMQoARmFpbGVkIHRvIGRlY29kZSB0aWxlIDEvMQoASW5zdWZmaWNpZW50IGRhdGEgZm9yIENNQVAgYm94LgoATmVlZCB0byByZWFkIGEgUENMUiBib3ggYmVmb3JlIHRoZSBDTUFQIGJveC4KAEluc3VmZmljaWVudCBkYXRhIGZvciBDREVGIGJveC4KAE51bWJlciBvZiBjaGFubmVsIGRlc2NyaXB0aW9uIGlzIGVxdWFsIHRvIHplcm8gaW4gQ0RFRiBib3guCgBTdHJlYW0gZXJyb3Igd2hpbGUgcmVhZGluZyBKUDIgSGVhZGVyIGJveDogbm8gJ2loZHInIGJveC4KAE5vbiBjb25mb3JtYW50IGNvZGVzdHJlYW0gVFBzb3Q9PVROc290LgoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3g6IGJveCBsZW5ndGggaXMgaW5jb25zaXN0ZW50LgoAQm94IGxlbmd0aCBpcyBpbmNvbnNpc3RlbnQuCgBSZXNvbHV0aW9uIGZhY3RvciBpcyBncmVhdGVyIHRoYW4gdGhlIG1heGltdW0gcmVzb2x1dGlvbiBpbiB0aGUgY29tcG9uZW50LgoAQ29tcG9uZW50IG1hcHBpbmcgc2VlbXMgd3JvbmcuIFRyeWluZyB0byBjb3JyZWN0LgoASW5jb21wbGV0ZSBjaGFubmVsIGRlZmluaXRpb25zLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gSW52YWxpZCBjb2RlYmxvY2sgbGVuZ3RoIHZhbHVlcy4KAFdlIGRvIG5vdCBzdXBwb3J0IG1vcmUgdGhhbiAzIGNvZGluZyBwYXNzZXMgaW4gYW4gSFQgY29kZWJsb2NrOyBUaGlzIGNvZGVibG9ja3MgaGFzICVkIHBhc3Nlcy4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFRoZXJlIGFyZSAlZCB6ZXJvIGJpdHBsYW5lcyBpbiAlZCBiaXRwbGFuZXMuCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgdHJhbnNmb3JtYXRpb24gc3RhZ2VzLgoAVW5rbm93biBtYXJrZXIgaGFzIGJlZW4gZGV0ZWN0ZWQgYW5kIGdlbmVyYXRlZCBlcnJvci4KAENvZGVjIHByb3ZpZGVkIHRvIHRoZSBvcGpfc2V0dXBfZGVjb2RlciBmdW5jdGlvbiBpcyBub3QgYSBkZWNvbXByZXNzb3IgaGFuZGxlci4KAENvZGVjIHByb3ZpZGVkIHRvIHRoZSBvcGpfcmVhZF9oZWFkZXIgZnVuY3Rpb24gaXMgbm90IGEgZGVjb21wcmVzc29yIGhhbmRsZXIuCgBUaWxlcyBkb24ndCBhbGwgaGF2ZSB0aGUgc2FtZSBkaW1lbnNpb24uIFNraXAgdGhlIE1DVCBzdGVwLgoATnVtYmVyIG9mIGNvbXBvbmVudHMgKCVkKSBpcyBpbmNvbnNpc3RlbnQgd2l0aCBhIE1DVC4gU2tpcCB0aGUgTUNUIHN0ZXAuCgBKUDIgYm94IHdoaWNoIGFyZSBhZnRlciB0aGUgY29kZXN0cmVhbSB3aWxsIG5vdCBiZSByZWFkIGJ5IHRoaXMgZnVuY3Rpb24uCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBXaGVuIHRoZSBudW1iZXIgb2YgemVybyBwbGFuZXMgYml0cGxhbmVzIGlzIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgYml0cGxhbmVzLCBvbmx5IHRoZSBjbGVhbnVwIHBhc3MgbWFrZXMgc2Vuc2UsIGJ1dCB3ZSBoYXZlICVkIHBhc3NlcyBpbiB0aGlzIGNvZGVibG9jay4gVGhlcmVmb3JlLCBvbmx5IHRoZSBjbGVhbnVwIHBhc3Mgd2lsbCBiZSBkZWNvZGVkLiBUaGlzIG1lc3NhZ2Ugd2lsbCBub3QgYmUgZGlzcGxheWVkIGFnYWluLgoASW1hZ2UgaGFzIGxlc3MgY29tcG9uZW50cyB0aGFuIGNvZGVzdHJlYW0uCgBOZWVkIHRvIGRlY29kZSB0aGUgbWFpbiBoZWFkZXIgYmVmb3JlIGJlZ2luIHRvIGRlY29kZSB0aGUgcmVtYWluaW5nIGNvZGVzdHJlYW0uCgBQc290IHZhbHVlIG9mIHRoZSBjdXJyZW50IHRpbGUtcGFydCBpcyBlcXVhbCB0byB6ZXJvLCB3ZSBhc3N1bWluZyBpdCBpcyB0aGUgbGFzdCB0aWxlLXBhcnQgb2YgdGhlIGNvZGVzdHJlYW0uCgBBIG1hbGZvcm1lZCBjb2RlYmxvY2sgdGhhdCBoYXMgbW9yZSB0aGFuIG9uZSBjb2RpbmcgcGFzcywgYnV0IHplcm8gbGVuZ3RoIGZvciAybmQgYW5kIHBvdGVudGlhbGx5IHRoZSAzcmQgcGFzcyBpbiBhbiBIVCBjb2RlYmxvY2suCgAJCQkgdGlsZS1wYXJ0WyVkXTogc3Rhcl9wb3M9JWxsaSwgZW5kX2hlYWRlcj0lbGxpLCBlbmRfcG9zPSVsbGkuCgBUaWxlICV1IGhhcyBUUHNvdCA9PSAwIGFuZCBUTnNvdCA9PSAwLCBidXQgbm8gb3RoZXIgdGlsZS1wYXJ0cyB3ZXJlIGZvdW5kLiBFT0MgaXMgYWxzbyBtaXNzaW5nLgoAQ29tcG9uZW50ICVkIGRvZXNuJ3QgaGF2ZSBhIG1hcHBpbmcuCgBBIGNvbmZvcm1pbmcgSlAyIHJlYWRlciBzaGFsbCBpZ25vcmUgYWxsIENvbG91ciBTcGVjaWZpY2F0aW9uIGJveGVzIGFmdGVyIHRoZSBmaXJzdCwgc28gd2UgaWdub3JlIHRoaXMgb25lLgoAVGhlIHNpZ25hdHVyZSBib3ggbXVzdCBiZSB0aGUgZmlyc3QgYm94IGluIHRoZSBmaWxlLgoAVGhlICBib3ggbXVzdCBiZSB0aGUgZmlyc3QgYm94IGluIHRoZSBmaWxlLgoAVGhlIGZ0eXAgYm94IG11c3QgYmUgdGhlIHNlY29uZCBib3ggaW4gdGhlIGZpbGUuCgBGYWlsZWQgdG8gZGVjb2RlLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gSW5jb3JyZWN0IE1FTCBzZWdtZW50IHNlcXVlbmNlLgoAQ29tcG9uZW50ICVkIGlzIG1hcHBlZCB0d2ljZS4KAE9ubHkgb25lIENNQVAgYm94IGlzIGFsbG93ZWQuCgBXZSBuZWVkIGFuIGltYWdlIHByZXZpb3VzbHkgY3JlYXRlZC4KAElIRFIgYm94X21pc3NpbmcuIFJlcXVpcmVkLgoASlAySCBib3ggbWlzc2luZy4gUmVxdWlyZWQuCgBOb3Qgc3VyZSBob3cgdGhhdCBoYXBwZW5lZC4KAE1haW4gaGVhZGVyIGhhcyBiZWVuIGNvcnJlY3RseSBkZWNvZGVkLgoAVGlsZSAlZC8lZCBoYXMgYmVlbiBkZWNvZGVkLgoASGVhZGVyIG9mIHRpbGUgJWQgLyAlZCBoYXMgYmVlbiByZWFkLgoARW1wdHkgU09UIG1hcmtlciBkZXRlY3RlZDogUHNvdD0lZC4KAERpcmVjdCB1c2UgYXQgIyVkIGhvd2V2ZXIgcGNvbD0lZC4KAEltcGxlbWVudGF0aW9uIGxpbWl0YXRpb246IGZvciBwYWxldHRlIG1hcHBpbmcsIHBjb2xbJWRdIHNob3VsZCBiZSBlcXVhbCB0byAlZCwgYnV0IGlzIGVxdWFsIHRvICVkLgoASW52YWxpZCBjb21wb25lbnQvcGFsZXR0ZSBpbmRleCBmb3IgZGlyZWN0IG1hcHBpbmcgJWQuCgBJbnZhbGlkIHZhbHVlIGZvciBjbWFwWyVkXS5tdHlwID0gJWQuCgBQc290IHZhbHVlIGlzIG5vdCBjb3JyZWN0IHJlZ2FyZHMgdG8gdGhlIEpQRUcyMDAwIG5vcm06ICVkLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gVkxDIGNvZGUgcHJvZHVjZXMgc2lnbmlmaWNhbnQgc2FtcGxlcyBvdXRzaWRlIHRoZSBjb2RlYmxvY2sgYXJlYS4KAFVuZXhwZWN0ZWQgT09NLgoAMzIgYml0cyBhcmUgbm90IGVub3VnaCB0byBkZWNvZGUgdGhpcyBjb2RlYmxvY2ssIHNpbmNlIHRoZSBudW1iZXIgb2YgYml0cGxhbmUsICVkLCBpcyBsYXJnZXIgdGhhbiAzMC4KAEJvdHRvbSBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTE9JWQpIHNob3VsZCBiZSA+IDAuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIHNob3VsZCBiZSA+IDAuCgBVcCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTA9JWQpIHNob3VsZCBiZSA+PSAwLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIHNob3VsZCBiZSA+PSAwLgoARXJyb3IgcmVhZGluZyBQUFQgbWFya2VyOiBwYWNrZXQgaGVhZGVyIGhhdmUgYmVlbiBwcmV2aW91c2x5IGZvdW5kIGluIHRoZSBtYWluIGhlYWRlciAoUFBNIG1hcmtlcikuCgBTdGFydCB0byByZWFkIGoyayBtYWluIGhlYWRlciAoJWxsZCkuCgBCb3R0b20gcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kxPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZc2l6PSVkKS4KAFVwIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MD0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWXNpej0lZCkuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhzaXo9JWQpLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhzaXo9JWQpLgoAQm90dG9tIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWU9zaXo9JWQpLgoAVXAgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZT3Npej0lZCkuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhPc2l6PSVkKS4KAExlZnQgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3gwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChYT3Npej0lZCkuCgBTaXplIHggb2YgdGhlIGRlY29kZWQgY29tcG9uZW50IGltYWdlIGlzIGluY29ycmVjdCAoY29tcFslZF0udz0lZCkuCgBTaXplIHkgb2YgdGhlIGRlY29kZWQgY29tcG9uZW50IGltYWdlIGlzIGluY29ycmVjdCAoY29tcFslZF0uaD0lZCkuCgBUaWxlIHJlYWQsIGRlY29kZWQgYW5kIHVwZGF0ZWQgaXMgbm90IHRoZSBkZXNpcmVkIG9uZSAoJWQgdnMgJWQpLgoASW52YWxpZCBjb21wb25lbnQgaW5kZXggJWQgKD49ICVkKS4KAG9wal9yZWFkX2hlYWRlcigpIHNob3VsZCBiZSBjYWxsZWQgYmVmb3JlIG9wal9zZXRfZGVjb2RlZF9jb21wb25lbnRzKCkuCgBNZW1vcnkgYWxsb2NhdGlvbiBmYWlsdXJlIGluIG9wal9qcDJfYXBwbHlfcGNscigpLgoAaW1hZ2UtPmNvbXBzWyVkXS5kYXRhID09IE5VTEwgaW4gb3BqX2pwMl9hcHBseV9wY2xyKCkuCgBpbnZhbGlkIGJveCBzaXplICVkICgleCkKAEZhaWwgdG8gcmVhZCB0aGUgY3VycmVudCBtYXJrZXIgc2VnbWVudCAoJSN4KQoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBJSERSIHcoJXUpIGgoJXUpIHZzLiBTSVogdygldSkgaCgldSkKAEVycm9yIHJlYWRpbmcgQ09DIG1hcmtlciAoYmFkIG51bWJlciBvZiBjb21wb25lbnRzKQoASW52YWxpZCBudW1iZXIgb2YgdGlsZXMgOiAldSB4ICV1IChtYXhpbXVtIGZpeGVkIGJ5IGpwZWcyMDAwIG5vcm0gaXMgNjU1MzUgdGlsZXMpCgBJbnZhbGlkIG51bWJlciBvZiBjb21wb25lbnRzIChpaGRyKQoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGltYWdlIGhlYWRlciAoaWhkcikKAFdyb25nIHZhbHVlcyBmb3I6IHcoJWQpIGgoJWQpIG51bWNvbXBzKCVkKSAoaWhkcikKAEludmFsaWQgdmFsdWVzIGZvciBjb21wID0gJWQgOiBkeD0ldSBkeT0ldSAoc2hvdWxkIGJlIGJldHdlZW4gMSBhbmQgMjU1IGFjY29yZGluZyB0byB0aGUgSlBFRzIwMDAgbm9ybSkKAEJhZCBpbWFnZSBoZWFkZXIgYm94IChiYWQgc2l6ZSkKAEJhZCBDT0xSIGhlYWRlciBib3ggKGJhZCBzaXplKQoAQmFkIEJQQ0MgaGVhZGVyIGJveCAoYmFkIHNpemUpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IG5lZ2F0aXZlIG9yIHplcm8gaW1hZ2Ugc2l6ZSAoJWxsZCB4ICVsbGQpCgBza2lwOiBzZWdtZW50IHRvbyBsb25nICglZCkgd2l0aCBtYXggKCVkKSBmb3IgY29kZWJsb2NrICVkIChwPSVkLCBiPSVkLCByPSVkLCBjPSVkKQoAcmVhZDogc2VnbWVudCB0b28gbG9uZyAoJWQpIHdpdGggbWF4ICglZCkgZm9yIGNvZGVibG9jayAlZCAocD0lZCwgYj0lZCwgcj0lZCwgYz0lZCkKAERlc3BpdGUgSlAyIEJQQyE9MjU1LCBwcmVjaXNpb24gYW5kL29yIHNnbmQgdmFsdWVzIGZvciBjb21wWyVkXSBpcyBkaWZmZXJlbnQgdGhhbiBjb21wWzBdOgogICAgICAgIFswXSBwcmVjKCVkKSBzZ25kKCVkKSBbJWRdIHByZWMoJWQpIHNnbmQoJWQpCgBiYWQgY29tcG9uZW50IG51bWJlciBpbiBSR04gKCVkIHdoZW4gdGhlcmUgYXJlIG9ubHkgJWQpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IG51bWJlciBvZiBjb21wb25lbnQgaXMgbm90IGNvbXBhdGlibGUgd2l0aCB0aGUgcmVtYWluaW5nIG51bWJlciBvZiBwYXJhbWV0ZXJzICggJWQgdnMgJWQpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IGludmFsaWQgdGlsZSBzaXplICh0ZHg6ICVkLCB0ZHk6ICVkKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoYmFkIHNpemU6ICVkKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoQ0lFTGFiLCBiYWQgc2l6ZTogJWQpCgBQVEVSTSBjaGVjayBmYWlsdXJlOiAlZCByZW1haW5pbmcgYnl0ZXMgaW4gY29kZSBibG9jayAoJWQgdXNlZCAvICVkKQoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gT25lIG9mIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uIGlzIG5vdCBtZXQ6IDIgPD0gU2N1cCA8PSBtaW4oTGN1cCwgNDA3OSkKAEludmFsaWQgdmFsdWVzIGZvciBjb21wID0gJWQgOiBwcmVjPSV1IChzaG91bGQgYmUgYmV0d2VlbiAxIGFuZCAzOCBhY2NvcmRpbmcgdG8gdGhlIEpQRUcyMDAwIG5vcm0uIE9wZW5KcGVnIG9ubHkgc3VwcG9ydHMgdXAgdG8gMzEpCgBJbnZhbGlkIGJpdCBudW1iZXIgJWQgaW4gb3BqX3QyX3JlYWRfcGFja2V0X2hlYWRlcigpCgBTdHJlYW0gZXJyb3IhCgBFcnJvciBvbiB3cml0aW5nIHN0cmVhbSEKAFN0cmVhbSByZWFjaGVkIGl0cyBlbmQgIQoARXhwZWN0ZWQgYSBTT0MgbWFya2VyIAoASW52YWxpZCBib3ggc2l6ZSAlZCBmb3IgYm94ICclYyVjJWMlYycuIE5lZWQgJWQgYnl0ZXMsICVkIGJ5dGVzIHJlbWFpbmluZyAKAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFVfcSBpcyBsYXJnZXIgdGhhbiB6ZXJvIGJpdHBsYW5lcyArIDEgCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBEZWNvZGluZyB0aGlzIGNvZGVibG9jayBpcyBzdG9wcGVkLiBVX3EgaXNsYXJnZXIgdGhhbiBiaXRwbGFuZXMgKyAxIAoAQ09MUiBCT1ggbWV0aCB2YWx1ZSBpcyBub3QgYSByZWd1bGFyIHZhbHVlICglZCksIHNvIHdlIHdpbGwgaWdub3JlIHRoZSBlbnRpcmUgQ29sb3VyIFNwZWNpZmljYXRpb24gYm94LiAKAFdoaWxlIHJlYWRpbmcgQ0NQX1FOVFNUWSBlbGVtZW50IGluc2lkZSBRQ0Qgb3IgUUNDIG1hcmtlciBzZWdtZW50LCBudW1iZXIgb2Ygc3ViYmFuZHMgKCVkKSBpcyBncmVhdGVyIHRvIE9QSl9KMktfTUFYQkFORFMgKCVkKS4gU28gd2UgbGltaXQgdGhlIG51bWJlciBvZiBlbGVtZW50cyBzdG9yZWQgdG8gT1BKX0oyS19NQVhCQU5EUyAoJWQpIGFuZCBza2lwIHRoZSByZXN0LiAKAEpQMiBJSERSIGJveDogY29tcHJlc3Npb24gdHlwZSBpbmRpY2F0ZSB0aGF0IHRoZSBmaWxlIGlzIG5vdCBhIGNvbmZvcm1pbmcgSlAyIGZpbGUgKCVkKSAKAFRpbGUgaW5kZXggcHJvdmlkZWQgYnkgdGhlIHVzZXIgaXMgaW5jb3JyZWN0ICVkIChtYXggPSAlZCkgCgBFcnJvciBkZWNvZGluZyBjb21wb25lbnQgJWQuClRoZSBudW1iZXIgb2YgcmVzb2x1dGlvbnMgdG8gcmVtb3ZlICglZCkgaXMgZ3JlYXRlciBvciBlcXVhbCB0aGFuIHRoZSBudW1iZXIgb2YgcmVzb2x1dGlvbnMgb2YgdGhpcyBjb21wb25lbnQg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AAABVAAAAQAAAABQAADAUAAAAUgAAAAAAAAQUAAAsFAAAAFIAAABAAAAIFAAAMBQAAABOAAAAAAAADBQAACwUAAAATgAAAEAAABAUAAAwFAAAAEwAAAAAAAAUFAAABBRAAABMAAAAQAAAGBQAAAgUQAAASQAAAAAAABwUAAAMFEAAAEkAAABAAAAgFAAAEBRAAABHAAAAAAAAJBQAABwUQAAARwAAAEAAACgUAAAgFEAAAEWAAAAAAAAkFIAAJBRAAABFgAAAQAAAKBSAACgUQAAAVYAAAAAAADQUAAAwFAAAAFWAAABAAAA4FAAALBQAAABVAAAAAAAAPBQAACwUAAAAVQAAAEAAAAAUQAAwFAAAAFRAAAAAAAAEFEAANBQAAABUQAAAQAAACBRAADgUAAAAUgAAAAAAAAwUQAA8FAAAAFIAAABAAAAQFEAAABRAAABOAAAAAAAAFBRAAAQUQAAATgAAAEAAABgUQAAIFEAAAE0AAAAAAAAcFEAADBRAAABNAAAAQAAAIBRAABAUQAAATAAAAAAAACQUQAAUFEAAAEwAAABAAAAoFEAAGBRAAABKAAAAAAAALBRAABQUQAAASgAAAEAAADAUQAAYFEAAAEkAAAAAAAA0FEAAHBRAAABJAAAAQAAAOBRAACAUQAAASIAAAAAAADwUQAAkFEAAAEiAAABAAAAAFIAAKBRAAABHAAAAAAAABBSAACwUQAAARwAAAEAAAAgUgAAwFEAAAEYAAAAAAAAMFIAANBRAAABGAAAAQAAAEBSAADgUQAAARYAAAAAAABQUgAA8FEAAAEWAAABAAAAYFIAAABSAAABFAAAAAAAAHBSAAAQUgAAARQAAAEAAACAUgAAIFIAAAESAAAAAAAAkFIAADBSAAABEgAAAQAAAKBSAABAUgAAAREAAAAAAACwUgAAUFIAAAERAAABAAAAwFIAAGBSAADBCgAAAAAAANBSAABwUgAAwQoAAAEAAADgUgAAgFIAAMEJAAAAAAAA8FIAAJBSAADBCQAAAQAAAABTAACgUgAAoQgAAAAAAAAQUwAAsFIAAKEIAAABAAAAIFMAAMBSAAAhBQAAAAAAADBTAADQUgAAIQUAAAEAAABAUwAA4FIAAEEEAAAAAAAAUFMAAPBSAABBBAAAAQAAAGBTAAAAUwAAoQIAAAAAAABwUwAAEFMAAKECAAABAAAAgFMAACBTAAAhAgAAAAAAAJBTAAAwUwAAIQIAAAEAAACgUwAAQFMAAEEBAAAAAAAAsFMAAFBTAABBAQAAAQAAAMBTAABgUwAAEQEAAAAAAADQUwAAcFMAABEBAAABAAAA4FMAAIBTAACFAAAAAAAAAPBTAACQUwAAhQAAAAEAAAAAVAAAoFMAAEkAAAAAAAAAEFQAALBTAABJAAAAAQAAACBUAADAUwAAJQAAAAAAAAAwVAAA0FMAACUAAAABAAAAQFQAAOBTAAAVAAAAAAAAAFBUAADwUwAAFQAAAAEAAABgVAAAAFQAAAkAAAAAAAAAcFQAABBUAAAJAAAAAQAAAIBUAAAgVAAABQAAAAAAAACQVAAAMFQAAAUAAAABAAAAoFQAAEBUAAABAAAAAAAAAJBUAABQVAAAAQAAAAEAAACgVAAAYFQAAAFWAAAAAAAAsFQAALBUAAABVgAAAQAAAMBUAADAVAAAAAEDAwECAwMFBgcHBgYHBwABAwMBAgMDBQYHBwYGBwcFBgcHBgYHBwgICAgICAgIBQYHBwYGBwcICAgICAgICAECAwMCAgMDBgYHBwYGBwcBAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAECAwMCAgMDBgYHBwYGBwcBAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgCAgMDAgIDAwYGBwcGBgcHAgIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgAAQUGAQIGBgMDBwcDAwcHAAEFBgECBgYDAwcHAwMHBwMDBwcDAwcHBAQHBwQEBwcDAwcHAwMHBwQEBwcEBAcHAQIGBgICBgYDAwcHAwMHBwECBgYCAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwUGCAgGBggIBwcICAcHCAgFBggIBgYICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgGBggIBgYICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIAQIGBgICBgYDAwcHAwMHBwECBgYCAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwICBgYCAgYGAwMHBwMDBwcCAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcGBggIBgYICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAABAwMBAgMDBQYHBwYGBwcAAQMDAQIDAwUGBwcGBgcHBQYHBwYGBwcICAgICAgICAUGBwcGBgcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAgIDAwICAwMGBgcHBgYHBwICAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAAMBBAMGBAcBBAIFBAcFBwADAQQDBgQHAQQCBQQHBQcBBAIFBAcFBwIFAgUFBwUHAQQCBQQHBQcCBQIFBQcFBwMGBAcGCAcIBAcFBwcIBwgDBgQHBggHCAQHBQcHCAcIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgBBAIFBAcFBwIFAgUFBwUHAQQCBQQHBQcCBQIFBQcFBwIFAgUFBwUHAgUCBQUHBQcCBQIFBQcFBwIFAgUFBwUHBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAMGBAcGCAcIBAcFBwcIBwgDBgQHBggHCAQHBQcHCAcIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgGCAcICAgICAcIBwgICAgIBggHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgJCQoKCQkKCgwMDQsMDA0LCQkKCgkJCgoMDAsNDAwLDQwMDQ0MDAsLDAkNCgkMCgsMDAsLDAwNDQwJCwoJDAoNCQkKCgkJCgoMDA0LDAwNCwkJCgoJCQoKDAwLDQwMCw0MDA0NDAwLCwwJDQoJDAoLDAwLCwwMDQ0MCQsKCQwKDQoKCgoKCgoKDQsNCw0LDQsKCgkJCgoJCQ0LDAwNCwwMDQ0NDQsLCwsNCg0KCgsKCw0NDAwLCwwMDQoMCQoLCQwKCgkJCgoJCQsNDAwLDQwMCgoKCgoKCgoLDQsNCw0LDQsLDAwNDQwMCwoMCQoNCQwLCwsLDQ0NDQsKCwoKDQoNAEHZuwELNwEAAQABAAEAAAEBAAABAQABAAEAAQABAAAAAAEBAQEAAAAAAAEAAQAAAAABAQEBAAAAAQABAQEAQZm8AQs3AQABAAEAAQAAAQEAAAEBAAEAAQABAAEAAAAAAQEBAQAAAAAAAQABAAAAAAEBAQEAAAABAAEBAQBB2bwBCwcBAAEAAQABAEHpvAELlQIBAAEAAQABAAAAAAEBAQEAAAAAAAEAAQAAAAABAQEBAAAAAAABAAEBAQAAAQEAAAABAAEAAQABAQEBAQEBAQEAAQABAAEAAQAAAAABAQEBAAEAAAEBAAEAAAAAAQEBAQABAAEBAQEBAgAAAAQAAAAEAAAACAAAAJD/AAAMAAAAGAAAAFL/AAAUAAAAGQAAAFP/AAAUAAAAGgAAAF7/AAAUAAAAGwAAAFz/AAAUAAAAHAAAAF3/AAAUAAAAHQAAAF//AAAUAAAAHgAAAFH/AAACAAAAHwAAAFX/AAAEAAAAIAAAAFf/AAAEAAAAIQAAAFj/AAAQAAAAIgAAAGD/AAAEAAAAIwAAAGH/AAAQAAAAJAAAAJH/AEGIvwELZWP/AAAEAAAAJQAAAGT/AAAUAAAAJgAAAHT/AAAUAAAAJwAAAHj/AAAEAAAAKAAAAFD/AAAEAAAAKQAAAFn/AAAEAAAAKgAAAHX/AAAUAAAAKwAAAHf/AAAUAAAALAAAAAAAAAAUAEGAwAELNS0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAAICBQajYAAABweXRmNwAAAGgycGo4AEHAwAELMnJkaGk5AAAAcmxvYzoAAABjY3BiOwAAAHJsY3A8AAAAcGFtYz0AAABmZWRjPgAAAPhiAEGAwQELQRkACwAZGRkAAAAABQAAAAAAAAkAAAAACwAAAAAAAAAAGQAKChkZGQMKBwABAAkLGAAACQYLAAALAAYZAAAAGRkZAEHRwQELIQ4AAAAAAAAAABkACw0ZGRkADQAAAgAJDgAAAAkADgAADgBBi8IBCwEMAEGXwgELFRMAAAAAEwAAAAAJDAAAAAAADAAADABBxcIBCwEQAEHRwgELFQ8AAAAEDwAAAAAJEAAAAAAAEAAAEABB/8IBCwESAEGLwwELHhEAAAAAEQAAAAAJEgAAAAAAEgAAEgAAGgAAABoaGgBBwsMBCw4aAAAAGhoaAAAAAAAACQBB88MBCwEUAEH/wwELFRcAAAAAFwAAAAAJFAAAAAAAFAAAFABBrcQBCwEWAEG5xAELJxUAAAAAFQAAAAAJFgAAAAAAFgAAFgAAMDEyMzQ1Njc4OUFCQ0RFRgBB4MQBCwmQbAEAAAAAAAUAQfTEAQsBaQBBjMUBCwpqAAAAawAAAHhoAEGkxQELAQIAQbTFAQsI//////////8AQfjFAQsBBQBBhMYBCwFsAEGcxgELDmoAAABtAAAAiGgAAAAEAEG0xgELAQEAQcTGAQsF/////wo=");return receiveInstance(instantiateSync(u,e)[0])}();N.q,a._malloc=N.r,a._free=N.s,a._jp2_decode=N.u;w=function runCaller(){b||run();b||(w=runCaller)};function run(){if(!(m>0)){!function preRun(){if(a.preRun){"function"==typeof a.preRun&&(a.preRun=[a.preRun]);for(;a.preRun.length;)e=a.preRun.shift(),d.unshift(e)}var e;callRuntimeCallbacks(d)}();if(!(m>0))if(a.setStatus){a.setStatus("Running...");setTimeout((function(){setTimeout((function(){a.setStatus("")}),1);doRun()}),1)}else doRun()}function doRun(){if(!b){b=!0;a.calledRun=!0;!function initRuntime(){callRuntimeCallbacks(f)}();t(a);a.onRuntimeInitialized&&a.onRuntimeInitialized();!function postRun(){if(a.postRun){"function"==typeof a.postRun&&(a.postRun=[a.postRun]);for(;a.postRun.length;)e=a.postRun.shift(),p.unshift(e)}var e;callRuntimeCallbacks(p)}()}}}if(a.preInit){"function"==typeof a.preInit&&(a.preInit=[a.preInit]);for(;a.preInit.length>0;)a.preInit.pop()()}run();return a});const hi=Ci;class JpxError extends ot{constructor(e){super(e,"JpxError")}}class JpxImage{static#y=null;static decode(e,t){t||={};this.#y||=hi({warn});const i=this.#y.decode(e,t);if("string"==typeof i)throw new JpxError(i);return i}static cleanup(){this.#y=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const i=t;t=e.getByte();if(65361===(i<<8|t)){e.skip(4);const t=e.getInt32()>>>0,i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0;e.skip(16);return{width:t-a,height:i-s,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}class JpxStream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(e){this.decodeImage(null,e)}decodeImage(e,t){if(this.eof)return this.buffer;e||=this.bytes;this.buffer=JpxImage.decode(e,t);this.bufferLength=this.buffer.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}class LZWStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.cachedData=0;this.bitsCached=0;const a=4096,s={earlyChange:i,codeLength:9,nextCode:258,dictionaryValues:new Uint8Array(a),dictionaryLengths:new Uint16Array(a),dictionaryPrevCodes:new Uint16Array(a),currentSequence:new Uint8Array(a),currentSequenceLength:0};for(let e=0;e<256;++e){s.dictionaryValues[e]=e;s.dictionaryLengths[e]=1}this.lzwState=s}readBits(e){let t=this.bitsCached,i=this.cachedData;for(;t<e;){const e=this.str.getByte();if(-1===e){this.eof=!0;return null}i=i<<8|e;t+=8}this.bitsCached=t-=e;this.cachedData=i;this.lastCode=null;return i>>>t&(1<<e)-1}readBlock(){let e,t,i,a=1024;const s=this.lzwState;if(!s)return;const r=s.earlyChange;let n=s.nextCode;const g=s.dictionaryValues,o=s.dictionaryLengths,c=s.dictionaryPrevCodes;let C=s.codeLength,h=s.prevCode;const l=s.currentSequence;let Q=s.currentSequenceLength,E=0,u=this.bufferLength,d=this.ensureBuffer(this.bufferLength+a);for(e=0;e<512;e++){const e=this.readBits(C),s=Q>0;if(e<256){l[0]=e;Q=1}else{if(!(e>=258)){if(256===e){C=9;n=258;Q=0;continue}this.eof=!0;delete this.lzwState;break}if(e<n){Q=o[e];for(t=Q-1,i=e;t>=0;t--){l[t]=g[i];i=c[i]}}else l[Q++]=l[0]}if(s){c[n]=h;o[n]=o[h]+1;g[n]=l[0];n++;C=n+r&n+r-1?C:0|Math.min(Math.log(n+r)/.6931471805599453+1,12)}h=e;E+=Q;if(a<E){do{a+=512}while(a<E);d=this.ensureBuffer(this.bufferLength+a)}for(t=0;t<Q;t++)d[u++]=l[t]}s.nextCode=n;s.codeLength=C;s.prevCode=h;s.currentSequenceLength=Q;this.bufferLength=u}}class PredictorStream extends DecodeStream{constructor(e,t,i){super(t);if(!(i instanceof Dict))return e;const a=this.predictor=i.get("Predictor")||1;if(a<=1)return e;if(2!==a&&(a<10||a>15))throw new FormatError(`Unsupported predictor: ${a}`);this.readBlock=2===a?this.readBlockTiff:this.readBlockPng;this.str=e;this.dict=e.dict;const s=this.colors=i.get("Colors")||1,r=this.bits=i.get("BPC","BitsPerComponent")||8,n=this.columns=i.get("Columns")||1;this.pixBytes=s*r+7>>3;this.rowBytes=n*s*r+7>>3;return this}readBlockTiff(){const e=this.rowBytes,t=this.bufferLength,i=this.ensureBuffer(t+e),a=this.bits,s=this.colors,r=this.str.getBytes(e);this.eof=!r.length;if(this.eof)return;let n,g=0,o=0,c=0,C=0,h=t;if(1===a&&1===s)for(n=0;n<e;++n){let e=r[n]^g;e^=e>>1;e^=e>>2;e^=e>>4;g=(1&e)<<7;i[h++]=e}else if(8===a){for(n=0;n<s;++n)i[h++]=r[n];for(;n<e;++n){i[h]=i[h-s]+r[n];h++}}else if(16===a){const t=2*s;for(n=0;n<t;++n)i[h++]=r[n];for(;n<e;n+=2){const e=((255&r[n])<<8)+(255&r[n+1])+((255&i[h-t])<<8)+(255&i[h-t+1]);i[h++]=e>>8&255;i[h++]=255&e}}else{const e=new Uint8Array(s+1),h=(1<<a)-1;let l=0,Q=t;const E=this.columns;for(n=0;n<E;++n)for(let t=0;t<s;++t){if(c<a){g=g<<8|255&r[l++];c+=8}e[t]=e[t]+(g>>c-a)&h;c-=a;o=o<<a|e[t];C+=a;if(C>=8){i[Q++]=o>>C-8&255;C-=8}}C>0&&(i[Q++]=(o<<8-C)+(g&(1<<8-C)-1))}this.bufferLength+=e}readBlockPng(){const e=this.rowBytes,t=this.pixBytes,i=this.str.getByte(),a=this.str.getBytes(e);this.eof=!a.length;if(this.eof)return;const s=this.bufferLength,r=this.ensureBuffer(s+e);let n=r.subarray(s-e,s);0===n.length&&(n=new Uint8Array(e));let g,o,c,C=s;switch(i){case 0:for(g=0;g<e;++g)r[C++]=a[g];break;case 1:for(g=0;g<t;++g)r[C++]=a[g];for(;g<e;++g){r[C]=r[C-t]+a[g]&255;C++}break;case 2:for(g=0;g<e;++g)r[C++]=n[g]+a[g]&255;break;case 3:for(g=0;g<t;++g)r[C++]=(n[g]>>1)+a[g];for(;g<e;++g){r[C]=(n[g]+r[C-t]>>1)+a[g]&255;C++}break;case 4:for(g=0;g<t;++g){o=n[g];c=a[g];r[C++]=o+c}for(;g<e;++g){o=n[g];const e=n[g-t],i=r[C-t],s=i+o-e;let h=s-i;h<0&&(h=-h);let l=s-o;l<0&&(l=-l);let Q=s-e;Q<0&&(Q=-Q);c=a[g];r[C++]=h<=l&&h<=Q?i+c:l<=Q?o+c:e+c}break;default:throw new FormatError(`Unsupported predictor: ${i}`)}this.bufferLength+=e}}class RunLengthStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict}readBlock(){const e=this.str.getBytes(2);if(!e||e.length<2||128===e[0]){this.eof=!0;return}let t,i=this.bufferLength,a=e[0];if(a<128){t=this.ensureBuffer(i+a+1);t[i++]=e[1];if(a>0){const e=this.str.getBytes(a);t.set(e,i);i+=a}}else{a=257-a;const s=e[1];t=this.ensureBuffer(i+a+1);for(let e=0;e<a;e++)t[i++]=s}this.bufferLength=i}}class Parser{constructor({lexer:e,xref:t,allowStreams:i=!1,recoveryMode:a=!1}){this.lexer=e;this.xref=t;this.allowStreams=i;this.recoveryMode=a;this.imageCache=Object.create(null);this._imageId=0;this.refill()}refill(){this.buf1=this.lexer.getObj();this.buf2=this.lexer.getObj()}shift(){if(this.buf2 instanceof Cmd&&"ID"===this.buf2.cmd){this.buf1=this.buf2;this.buf2=null}else{this.buf1=this.buf2;this.buf2=this.lexer.getObj()}}tryShift(){try{this.shift();return!0}catch(e){if(e instanceof MissingDataException)throw e;return!1}}getObj(e=null){const t=this.buf1;this.shift();if(t instanceof Cmd)switch(t.cmd){case"BI":return this.makeInlineImage(e);case"[":const i=[];for(;!isCmd(this.buf1,"]")&&this.buf1!==wt;)i.push(this.getObj(e));if(this.buf1===wt){if(this.recoveryMode)return i;throw new ParserEOFException("End of file inside array.")}this.shift();return i;case"<<":const a=new Dict(this.xref);for(;!isCmd(this.buf1,">>")&&this.buf1!==wt;){if(!(this.buf1 instanceof Name)){info("Malformed dictionary: key must be a name object");this.shift();continue}const t=this.buf1.name;this.shift();if(this.buf1===wt)break;a.set(t,this.getObj(e))}if(this.buf1===wt){if(this.recoveryMode)return a;throw new ParserEOFException("End of file inside dictionary.")}if(isCmd(this.buf2,"stream"))return this.allowStreams?this.makeStream(a,e):a;this.shift();return a;default:return t}if(Number.isInteger(t)){if(Number.isInteger(this.buf1)&&isCmd(this.buf2,"R")){const e=Ref.get(t,this.buf1);this.shift();this.shift();return e}return t}return"string"==typeof t&&e?e.decryptString(t):t}findDefaultInlineStreamEnd(e){const{knownCommands:t}=this.lexer,i=e.pos;let a,s,r=0;for(;-1!==(a=e.getByte());)if(0===r)r=69===a?1:0;else if(1===r)r=73===a?2:0;else if(32===a||10===a||13===a){s=e.pos;const i=e.peekBytes(15),n=i.length;if(0===n)break;for(let e=0;e<n;e++){a=i[e];if((0!==a||0===i[e+1])&&(10!==a&&13!==a&&(a<32||a>127))){r=0;break}}if(2!==r)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const g=new Lexer(new Stream(i.slice()),t);g._hexStringWarn=()=>{};let o=0;for(;;){const e=g.getObj();if(e===wt){r=0;break}if(e instanceof Cmd){const i=t[e.cmd];if(!i){r=0;break}if(i.variableArgs?o<=i.numArgs:o===i.numArgs)break;o=0}else o++}if(2===r)break}else r=0;if(-1===a){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(s){warn('... trying to recover by using the last "EI" occurrence.');e.skip(-(e.pos-s))}}let n=4;e.skip(-n);a=e.peekByte();e.skip(n);isWhiteSpace(a)||n--;return e.pos-n-i}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let i,a,s=!1;for(;-1!==(i=e.getByte());)if(255===i){switch(e.getByte()){case 0:break;case 255:e.skip(-1);break;case 217:s=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:a=e.getUint16();a>2?e.skip(a-2):e.skip(-2)}if(s)break}const r=e.pos-t;if(-1===i){warn("Inline DCTDecode image stream: EOI marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte());)if(126===i){const t=e.pos;i=e.peekByte();for(;isWhiteSpace(i);){e.skip();i=e.peekByte()}if(62===i){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const a=e.pos-t;if(-1===i){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte())&&62!==i;);const a=e.pos-t;if(-1===i){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}inlineStreamSkipEI(e){let t,i=0;for(;-1!==(t=e.getByte());)if(0===i)i=69===t?1:0;else if(1===i)i=73===t?2:0;else if(2===i)break}makeInlineImage(e){const t=this.lexer,i=t.stream,a=Object.create(null);let s;for(;!isCmd(this.buf1,"ID")&&this.buf1!==wt;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===wt)break;a[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(s=i.pos-t.beginInlineImagePos);const r=this.xref.fetchIfRef(a.F||a.Filter);let n;if(r instanceof Name)n=r.name;else if(Array.isArray(r)){const e=this.xref.fetchIfRef(r[0]);e instanceof Name&&(n=e.name)}const g=i.pos;let o,c;switch(n){case"DCT":case"DCTDecode":o=this.findDCTDecodeInlineStreamEnd(i);break;case"A85":case"ASCII85Decode":o=this.findASCII85DecodeInlineStreamEnd(i);break;case"AHx":case"ASCIIHexDecode":o=this.findASCIIHexDecodeInlineStreamEnd(i);break;default:o=this.findDefaultInlineStreamEnd(i)}if(o<1e3&&s>0){const e=i.pos;i.pos=t.beginInlineImagePos;c=function getInlineImageCacheKey(e){const t=[],i=e.length;let a=0;for(;a<i-1;)t.push(e[a++]<<8|e[a++]);a<i&&t.push(e[a]);return i+"_"+String.fromCharCode.apply(null,t)}(i.getBytes(s+o));i.pos=e;const a=this.imageCache[c];if(void 0!==a){this.buf2=Cmd.get("EI");this.shift();a.reset();return a}}const C=new Dict(this.xref);for(const e in a)C.set(e,a[e]);let h=i.makeSubStream(g,o,C);e&&(h=e.createStream(h,o));h=this.filter(h,C,o);h.dict=C;if(void 0!==c){h.cacheKey="inline_img_"+ ++this._imageId;this.imageCache[c]=h}this.buf2=Cmd.get("EI");this.shift();return h}#w(e){const{stream:t}=this.lexer;t.pos=e;const i=new Uint8Array([101,110,100]),a=i.length,s=[new Uint8Array([115,116,114,101,97,109]),new Uint8Array([115,116,101,97,109]),new Uint8Array([115,116,114,101,97])],r=9-a;for(;t.pos<t.end;){const n=t.peekBytes(2048),g=n.length-9;if(g<=0)break;let o=0;for(;o<g;){let g=0;for(;g<a&&n[o+g]===i[g];)g++;if(g>=a){let a=!1;for(const e of s){const t=e.length;let s=0;for(;s<t&&n[o+g+s]===e[s];)s++;if(s>=r){a=!0;break}if(s>=t){if(isWhiteSpace(n[o+g+s])){info(`Found "${bytesToString([...i,...e])}" when searching for endstream command.`);a=!0}break}}if(a){t.pos+=o;return t.pos-e}}o++}t.pos+=g}return-1}makeStream(e,t){const i=this.lexer;let a=i.stream;i.skipToNextLine();const s=a.pos-1;let r=e.get("Length");if(!Number.isInteger(r)){info(`Bad length "${r&&r.toString()}" in stream.`);r=0}a.pos=s+r;i.nextChar();if(this.tryShift()&&isCmd(this.buf2,"endstream"))this.shift();else{r=this.#w(s);if(r<0)throw new FormatError("Missing endstream command.");i.nextChar();this.shift();this.shift()}this.shift();a=a.makeSubStream(s,r,e);t&&(a=t.createStream(a,r));a=this.filter(a,e,r);a.dict=e;return a}filter(e,t,i){let a=t.get("F","Filter"),s=t.get("DP","DecodeParms");if(a instanceof Name){Array.isArray(s)&&warn("/DecodeParms should not be an Array, when /Filter is a Name.");return this.makeFilter(e,a.name,i,s)}let r=i;if(Array.isArray(a)){const t=a,i=s;for(let n=0,g=t.length;n<g;++n){a=this.xref.fetchIfRef(t[n]);if(!(a instanceof Name))throw new FormatError(`Bad filter name "${a}"`);s=null;Array.isArray(i)&&n in i&&(s=this.xref.fetchIfRef(i[n]));e=this.makeFilter(e,a.name,r,s);r=null}}return e}makeFilter(e,t,i,a){if(0===i){warn(`Empty "${t}" stream.`);return new NullStream}try{switch(t){case"Fl":case"FlateDecode":return a?new PredictorStream(new FlateStream(e,i),i,a):new FlateStream(e,i);case"LZW":case"LZWDecode":let t=1;if(a){a.has("EarlyChange")&&(t=a.get("EarlyChange"));return new PredictorStream(new LZWStream(e,i,t),i,a)}return new LZWStream(e,i,t);case"DCT":case"DCTDecode":return new JpegStream(e,i,a);case"JPX":case"JPXDecode":return new JpxStream(e,i,a);case"A85":case"ASCII85Decode":return new Ascii85Stream(e,i);case"AHx":case"ASCIIHexDecode":return new AsciiHexStream(e,i);case"CCF":case"CCITTFaxDecode":return new CCITTFaxStream(e,i,a);case"RL":case"RunLengthDecode":return new RunLengthStream(e,i);case"JBIG2Decode":return new Jbig2Stream(e,i,a)}warn(`Filter "${t}" is not supported.`);return e}catch(e){if(e instanceof MissingDataException)throw e;warn(`Invalid stream: "${e}"`);return new NullStream}}}const Bi=[1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];function toHexDigit(e){return e>=48&&e<=57?15&e:e>=65&&e<=70||e>=97&&e<=102?9+(15&e):-1}class Lexer{constructor(e,t=null){this.stream=e;this.nextChar();this.strBuf=[];this.knownCommands=t;this._hexStringNumWarn=0;this.beginInlineImagePos=-1}nextChar(){return this.currentChar=this.stream.getByte()}peekChar(){return this.stream.peekByte()}getNumber(){let e=this.currentChar,t=!1,i=0,a=1;if(45===e){a=-1;e=this.nextChar();45===e&&(e=this.nextChar())}else 43===e&&(e=this.nextChar());if(10===e||13===e)do{e=this.nextChar()}while(10===e||13===e);if(46===e){i=10;e=this.nextChar()}if(e<48||e>57){const t=`Invalid number: ${String.fromCharCode(e)} (charCode ${e})`;if(isWhiteSpace(e)||-1===e){info(`Lexer.getNumber - "${t}".`);return 0}throw new FormatError(t)}let s=e-48,r=0,n=1;for(;(e=this.nextChar())>=0;)if(e>=48&&e<=57){const a=e-48;if(t)r=10*r+a;else{0!==i&&(i*=10);s=10*s+a}}else if(46===e){if(0!==i)break;i=1}else if(45===e)warn("Badly formatted number: minus sign in the middle");else{if(69!==e&&101!==e)break;e=this.peekChar();if(43===e||45===e){n=45===e?-1:1;this.nextChar()}else if(e<48||e>57)break;t=!0}0!==i&&(s/=i);t&&(s*=10**(n*r));return a*s}getString(){let e=1,t=!1;const i=this.strBuf;i.length=0;let a=this.nextChar();for(;;){let s=!1;switch(0|a){case-1:warn("Unterminated string");t=!0;break;case 40:++e;i.push("(");break;case 41:if(0==--e){this.nextChar();t=!0}else i.push(")");break;case 92:a=this.nextChar();switch(a){case-1:warn("Unterminated string");t=!0;break;case 110:i.push("\n");break;case 114:i.push("\r");break;case 116:i.push("\t");break;case 98:i.push("\b");break;case 102:i.push("\f");break;case 92:case 40:case 41:i.push(String.fromCharCode(a));break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:let e=15&a;a=this.nextChar();s=!0;if(a>=48&&a<=55){e=(e<<3)+(15&a);a=this.nextChar();if(a>=48&&a<=55){s=!1;e=(e<<3)+(15&a)}}i.push(String.fromCharCode(e));break;case 13:10===this.peekChar()&&this.nextChar();break;case 10:break;default:i.push(String.fromCharCode(a))}break;default:i.push(String.fromCharCode(a))}if(t)break;s||(a=this.nextChar())}return i.join("")}getName(){let e,t;const i=this.strBuf;i.length=0;for(;(e=this.nextChar())>=0&&!Bi[e];)if(35===e){e=this.nextChar();if(Bi[e]){warn("Lexer_getName: NUMBER SIGN (#) should be followed by a hexadecimal number.");i.push("#");break}const a=toHexDigit(e);if(-1!==a){t=e;e=this.nextChar();const s=toHexDigit(e);if(-1===s){warn(`Lexer_getName: Illegal digit (${String.fromCharCode(e)}) in hexadecimal number.`);i.push("#",String.fromCharCode(t));if(Bi[e])break;i.push(String.fromCharCode(e));continue}i.push(String.fromCharCode(a<<4|s))}else i.push("#",String.fromCharCode(e))}else i.push(String.fromCharCode(e));i.length>127&&warn(`Name token is longer than allowed by the spec: ${i.length}`);return Name.get(i.join(""))}_hexStringWarn(e){5!=this._hexStringNumWarn++?this._hexStringNumWarn>5||warn(`getHexString - ignoring invalid character: ${e}`):warn("getHexString - ignoring additional invalid characters.")}getHexString(){const e=this.strBuf;e.length=0;let t=this.currentChar,i=-1,a=-1;this._hexStringNumWarn=0;for(;;){if(t<0){warn("Unterminated hex string");break}if(62===t){this.nextChar();break}if(1!==Bi[t]){a=toHexDigit(t);if(-1===a)this._hexStringWarn(t);else if(-1===i)i=a;else{e.push(String.fromCharCode(i<<4|a));i=-1}t=this.nextChar()}else t=this.nextChar()}-1!==i&&e.push(String.fromCharCode(i<<4));return e.join("")}getObj(){let e=!1,t=this.currentChar;for(;;){if(t<0)return wt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(1!==Bi[t])break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return this.getNumber();case 40:return this.getString();case 47:return this.getName();case 91:this.nextChar();return Cmd.get("[");case 93:this.nextChar();return Cmd.get("]");case 60:t=this.nextChar();if(60===t){this.nextChar();return Cmd.get("<<")}return this.getHexString();case 62:t=this.nextChar();if(62===t){this.nextChar();return Cmd.get(">>")}return Cmd.get(">");case 123:this.nextChar();return Cmd.get("{");case 125:this.nextChar();return Cmd.get("}");case 41:this.nextChar();throw new FormatError(`Illegal character: ${t}`)}let i=String.fromCharCode(t);if(t<32||t>127){const e=this.peekChar();if(e>=32&&e<=127){this.nextChar();return Cmd.get(i)}}const a=this.knownCommands;let s=void 0!==a?.[i];for(;(t=this.nextChar())>=0&&!Bi[t];){const e=i+String.fromCharCode(t);if(s&&void 0===a[e])break;if(128===i.length)throw new FormatError(`Command token too long: ${i.length}`);i=e;s=void 0!==a?.[i]}if("true"===i)return!0;if("false"===i)return!1;if("null"===i)return null;"BI"===i&&(this.beginInlineImagePos=this.stream.pos);return Cmd.get(i)}skipToNextLine(){let e=this.currentChar;for(;e>=0;){if(13===e){e=this.nextChar();10===e&&this.nextChar();break}if(10===e){this.nextChar();break}e=this.nextChar()}}}class Linearization{static create(e){function getInt(e,t,i=!1){const a=e.get(t);if(Number.isInteger(a)&&(i?a>=0:a>0))return a;throw new Error(`The "${t}" parameter in the linearization dictionary is invalid.`)}const t=new Parser({lexer:new Lexer(e),xref:null}),i=t.getObj(),a=t.getObj(),s=t.getObj(),r=t.getObj();let n,g;if(!(Number.isInteger(i)&&Number.isInteger(a)&&isCmd(s,"obj")&&r instanceof Dict&&"number"==typeof(n=r.get("Linearized"))&&n>0))return null;if((g=getInt(r,"L"))!==e.length)throw new Error('The "L" parameter in the linearization dictionary does not equal the stream length.');return{length:g,hints:function getHints(e){const t=e.get("H");let i;if(Array.isArray(t)&&(2===(i=t.length)||4===i)){for(let e=0;e<i;e++){const i=t[e];if(!(Number.isInteger(i)&&i>0))throw new Error(`Hint (${e}) in the linearization dictionary is invalid.`)}return t}throw new Error("Hint array in the linearization dictionary is invalid.")}(r),objectNumberFirst:getInt(r,"O"),endFirst:getInt(r,"E"),numPages:getInt(r,"N"),mainXRefEntriesOffset:getInt(r,"T"),pageFirst:r.has("P")?getInt(r,"P",!0):0}}}const li=["Adobe-GB1-UCS2","Adobe-CNS1-UCS2","Adobe-Japan1-UCS2","Adobe-Korea1-UCS2","78-EUC-H","78-EUC-V","78-H","78-RKSJ-H","78-RKSJ-V","78-V","78ms-RKSJ-H","78ms-RKSJ-V","83pv-RKSJ-H","90ms-RKSJ-H","90ms-RKSJ-V","90msp-RKSJ-H","90msp-RKSJ-V","90pv-RKSJ-H","90pv-RKSJ-V","Add-H","Add-RKSJ-H","Add-RKSJ-V","Add-V","Adobe-CNS1-0","Adobe-CNS1-1","Adobe-CNS1-2","Adobe-CNS1-3","Adobe-CNS1-4","Adobe-CNS1-5","Adobe-CNS1-6","Adobe-GB1-0","Adobe-GB1-1","Adobe-GB1-2","Adobe-GB1-3","Adobe-GB1-4","Adobe-GB1-5","Adobe-Japan1-0","Adobe-Japan1-1","Adobe-Japan1-2","Adobe-Japan1-3","Adobe-Japan1-4","Adobe-Japan1-5","Adobe-Japan1-6","Adobe-Korea1-0","Adobe-Korea1-1","Adobe-Korea1-2","B5-H","B5-V","B5pc-H","B5pc-V","CNS-EUC-H","CNS-EUC-V","CNS1-H","CNS1-V","CNS2-H","CNS2-V","ETHK-B5-H","ETHK-B5-V","ETen-B5-H","ETen-B5-V","ETenms-B5-H","ETenms-B5-V","EUC-H","EUC-V","Ext-H","Ext-RKSJ-H","Ext-RKSJ-V","Ext-V","GB-EUC-H","GB-EUC-V","GB-H","GB-V","GBK-EUC-H","GBK-EUC-V","GBK2K-H","GBK2K-V","GBKp-EUC-H","GBKp-EUC-V","GBT-EUC-H","GBT-EUC-V","GBT-H","GBT-V","GBTpc-EUC-H","GBTpc-EUC-V","GBpc-EUC-H","GBpc-EUC-V","H","HKdla-B5-H","HKdla-B5-V","HKdlb-B5-H","HKdlb-B5-V","HKgccs-B5-H","HKgccs-B5-V","HKm314-B5-H","HKm314-B5-V","HKm471-B5-H","HKm471-B5-V","HKscs-B5-H","HKscs-B5-V","Hankaku","Hiragana","KSC-EUC-H","KSC-EUC-V","KSC-H","KSC-Johab-H","KSC-Johab-V","KSC-V","KSCms-UHC-H","KSCms-UHC-HW-H","KSCms-UHC-HW-V","KSCms-UHC-V","KSCpc-EUC-H","KSCpc-EUC-V","Katakana","NWP-H","NWP-V","RKSJ-H","RKSJ-V","Roman","UniCNS-UCS2-H","UniCNS-UCS2-V","UniCNS-UTF16-H","UniCNS-UTF16-V","UniCNS-UTF32-H","UniCNS-UTF32-V","UniCNS-UTF8-H","UniCNS-UTF8-V","UniGB-UCS2-H","UniGB-UCS2-V","UniGB-UTF16-H","UniGB-UTF16-V","UniGB-UTF32-H","UniGB-UTF32-V","UniGB-UTF8-H","UniGB-UTF8-V","UniJIS-UCS2-H","UniJIS-UCS2-HW-H","UniJIS-UCS2-HW-V","UniJIS-UCS2-V","UniJIS-UTF16-H","UniJIS-UTF16-V","UniJIS-UTF32-H","UniJIS-UTF32-V","UniJIS-UTF8-H","UniJIS-UTF8-V","UniJIS2004-UTF16-H","UniJIS2004-UTF16-V","UniJIS2004-UTF32-H","UniJIS2004-UTF32-V","UniJIS2004-UTF8-H","UniJIS2004-UTF8-V","UniJISPro-UCS2-HW-V","UniJISPro-UCS2-V","UniJISPro-UTF8-V","UniJISX0213-UTF32-H","UniJISX0213-UTF32-V","UniJISX02132004-UTF32-H","UniJISX02132004-UTF32-V","UniKS-UCS2-H","UniKS-UCS2-V","UniKS-UTF16-H","UniKS-UTF16-V","UniKS-UTF32-H","UniKS-UTF32-V","UniKS-UTF8-H","UniKS-UTF8-V","V","WP-Symbol"],Qi=2**24-1;class CMap{constructor(e=!1){this.codespaceRanges=[[],[],[],[]];this.numCodespaceRanges=0;this._map=[];this.name="";this.vertical=!1;this.useCMap=null;this.builtInCMap=e}addCodespaceRange(e,t,i){this.codespaceRanges[e-1].push(t,i);this.numCodespaceRanges++}mapCidRange(e,t,i){if(t-e>Qi)throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE.");for(;e<=t;)this._map[e++]=i++}mapBfRange(e,t,i){if(t-e>Qi)throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE.");const a=i.length-1;for(;e<=t;){this._map[e++]=i;const t=i.charCodeAt(a)+1;t>255?i=i.substring(0,a-1)+String.fromCharCode(i.charCodeAt(a-1)+1)+"\0":i=i.substring(0,a)+String.fromCharCode(t)}}mapBfRangeToArray(e,t,i){if(t-e>Qi)throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.");const a=i.length;let s=0;for(;e<=t&&s<a;){this._map[e]=i[s++];++e}}mapOne(e,t){this._map[e]=t}lookup(e){return this._map[e]}contains(e){return void 0!==this._map[e]}forEach(e){const t=this._map,i=t.length;if(i<=65536)for(let a=0;a<i;a++)void 0!==t[a]&&e(a,t[a]);else for(const i in t)e(i,t[i])}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}getMap(){return this._map}readCharCode(e,t,i){let a=0;const s=this.codespaceRanges;for(let r=0,n=s.length;r<n;r++){a=(a<<8|e.charCodeAt(t+r))>>>0;const n=s[r];for(let e=0,t=n.length;e<t;){const t=n[e++],s=n[e++];if(a>=t&&a<=s){i.charcode=a;i.length=r+1;return}}}i.charcode=0;i.length=1}getCharCodeLength(e){const t=this.codespaceRanges;for(let i=0,a=t.length;i<a;i++){const a=t[i];for(let t=0,s=a.length;t<s;){const s=a[t++],r=a[t++];if(e>=s&&e<=r)return i+1}}return 1}get length(){return this._map.length}get isIdentityCMap(){if("Identity-H"!==this.name&&"Identity-V"!==this.name)return!1;if(65536!==this._map.length)return!1;for(let e=0;e<65536;e++)if(this._map[e]!==e)return!1;return!0}}class IdentityCMap extends CMap{constructor(e,t){super();this.vertical=e;this.addCodespaceRange(t,0,65535)}mapCidRange(e,t,i){unreachable("should not call mapCidRange")}mapBfRange(e,t,i){unreachable("should not call mapBfRange")}mapBfRangeToArray(e,t,i){unreachable("should not call mapBfRangeToArray")}mapOne(e,t){unreachable("should not call mapCidOne")}lookup(e){return Number.isInteger(e)&&e<=65535?e:void 0}contains(e){return Number.isInteger(e)&&e<=65535}forEach(e){for(let t=0;t<=65535;t++)e(t,t)}charCodeOf(e){return Number.isInteger(e)&&e<=65535?e:-1}getMap(){const e=new Array(65536);for(let t=0;t<=65535;t++)e[t]=t;return e}get length(){return 65536}get isIdentityCMap(){unreachable("should not access .isIdentityCMap")}}function strToInt(e){let t=0;for(let i=0;i<e.length;i++)t=t<<8|e.charCodeAt(i);return t>>>0}function expectString(e){if("string"!=typeof e)throw new FormatError("Malformed CMap: expected string.")}function expectInt(e){if(!Number.isInteger(e))throw new FormatError("Malformed CMap: expected int.")}function parseBfChar(e,t){for(;;){let i=t.getObj();if(i===wt)break;if(isCmd(i,"endbfchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=i;e.mapOne(a,s)}}function parseBfRange(e,t){for(;;){let i=t.getObj();if(i===wt)break;if(isCmd(i,"endbfrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();if(Number.isInteger(i)||"string"==typeof i){const t=Number.isInteger(i)?String.fromCharCode(i):i;e.mapBfRange(a,s,t)}else{if(!isCmd(i,"["))break;{i=t.getObj();const r=[];for(;!isCmd(i,"]")&&i!==wt;){r.push(i);i=t.getObj()}e.mapBfRangeToArray(a,s,r)}}}throw new FormatError("Invalid bf range.")}function parseCidChar(e,t){for(;;){let i=t.getObj();if(i===wt)break;if(isCmd(i,"endcidchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectInt(i);const s=i;e.mapOne(a,s)}}function parseCidRange(e,t){for(;;){let i=t.getObj();if(i===wt)break;if(isCmd(i,"endcidrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();expectInt(i);const r=i;e.mapCidRange(a,s,r)}}function parseCodespaceRange(e,t){for(;;){let i=t.getObj();if(i===wt)break;if(isCmd(i,"endcodespacerange"))return;if("string"!=typeof i)break;const a=strToInt(i);i=t.getObj();if("string"!=typeof i)break;const s=strToInt(i);e.addCodespaceRange(i.length,a,s)}throw new FormatError("Invalid codespace range.")}function parseWMode(e,t){const i=t.getObj();Number.isInteger(i)&&(e.vertical=!!i)}function parseCMapName(e,t){const i=t.getObj();i instanceof Name&&(e.name=i.name)}async function parseCMap(e,t,i,a){let s,r;A:for(;;)try{const i=t.getObj();if(i===wt)break;if(i instanceof Name){"WMode"===i.name?parseWMode(e,t):"CMapName"===i.name&&parseCMapName(e,t);s=i}else if(i instanceof Cmd)switch(i.cmd){case"endcmap":break A;case"usecmap":s instanceof Name&&(r=s.name);break;case"begincodespacerange":parseCodespaceRange(e,t);break;case"beginbfchar":parseBfChar(e,t);break;case"begincidchar":parseCidChar(e,t);break;case"beginbfrange":parseBfRange(e,t);break;case"begincidrange":parseCidRange(e,t)}}catch(e){if(e instanceof MissingDataException)throw e;warn("Invalid cMap data: "+e);continue}!a&&r&&(a=r);return a?extendCMap(e,i,a):e}async function extendCMap(e,t,i){e.useCMap=await createBuiltInCMap(i,t);if(0===e.numCodespaceRanges){const t=e.useCMap.codespaceRanges;for(let i=0;i<t.length;i++)e.codespaceRanges[i]=t[i].slice();e.numCodespaceRanges=e.useCMap.numCodespaceRanges}e.useCMap.forEach((function(t,i){e.contains(t)||e.mapOne(t,e.useCMap.lookup(t))}));return e}async function createBuiltInCMap(e,t){if("Identity-H"===e)return new IdentityCMap(!1,2);if("Identity-V"===e)return new IdentityCMap(!0,2);if(!li.includes(e))throw new Error("Unknown CMap name: "+e);if(!t)throw new Error("Built-in CMap parameters are not provided.");const{cMapData:i,compressionType:a}=await t(e),s=new CMap(!0);if(a===yA.BINARY)return(new BinaryCMapReader).process(i,s,(e=>extendCMap(s,t,e)));if(a===yA.NONE){const e=new Lexer(new Stream(i));return parseCMap(s,e,t,null)}throw new Error(`Invalid CMap "compressionType" value: ${a}`)}class CMapFactory{static async create({encoding:e,fetchBuiltInCMap:t,useCMap:i}){if(e instanceof Name)return createBuiltInCMap(e.name,t);if(e instanceof BaseStream){const a=await parseCMap(new CMap,new Lexer(e),t,i);return a.isIdentityCMap?createBuiltInCMap(a.name,t):a}throw new Error("Encoding required.")}}const Ei=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron"],ui=[".notdef","space","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],di=[".notdef","space","dollaroldstyle","dollarsuperior","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","hyphensuperior","colonmonetary","onefitted","rupiah","centoldstyle","figuredash","hypheninferior","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior"],fi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],pi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","centoldstyle","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","","threequartersemdash","","questionsmall","","","","","Ethsmall","","","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","","","","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hypheninferior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","asuperior","centsuperior","","","","","Aacutesmall","Agravesmall","Acircumflexsmall","Adieresissmall","Atildesmall","Aringsmall","Ccedillasmall","Eacutesmall","Egravesmall","Ecircumflexsmall","Edieresissmall","Iacutesmall","Igravesmall","Icircumflexsmall","Idieresissmall","Ntildesmall","Oacutesmall","Ogravesmall","Ocircumflexsmall","Odieresissmall","Otildesmall","Uacutesmall","Ugravesmall","Ucircumflexsmall","Udieresissmall","","eightsuperior","fourinferior","threeinferior","sixinferior","eightinferior","seveninferior","Scaronsmall","","centinferior","twoinferior","","Dieresissmall","","Caronsmall","osuperior","fiveinferior","","commainferior","periodinferior","Yacutesmall","","dollarinferior","","","Thornsmall","","nineinferior","zeroinferior","Zcaronsmall","AEsmall","Oslashsmall","questiondownsmall","oneinferior","Lslashsmall","","","","","","","Cedillasmall","","","","","","OEsmall","figuredash","hyphensuperior","","","","","exclamdownsmall","","Ydieresissmall","","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","ninesuperior","zerosuperior","","esuperior","rsuperior","tsuperior","","","isuperior","ssuperior","dsuperior","","","","","","lsuperior","Ogoneksmall","Brevesmall","Macronsmall","bsuperior","nsuperior","msuperior","commasuperior","periodsuperior","Dotaccentsmall","Ringsmall","","","",""],mi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","space","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron"],yi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls","","","",""],wi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","bullet","Euro","bullet","quotesinglbase","florin","quotedblbase","ellipsis","dagger","daggerdbl","circumflex","perthousand","Scaron","guilsinglleft","OE","bullet","Zcaron","bullet","bullet","quoteleft","quoteright","quotedblleft","quotedblright","bullet","endash","emdash","tilde","trademark","scaron","guilsinglright","oe","bullet","zcaron","Ydieresis","space","exclamdown","cent","sterling","currency","yen","brokenbar","section","dieresis","copyright","ordfeminine","guillemotleft","logicalnot","hyphen","registered","macron","degree","plusminus","twosuperior","threesuperior","acute","mu","paragraph","periodcentered","cedilla","onesuperior","ordmasculine","guillemotright","onequarter","onehalf","threequarters","questiondown","Agrave","Aacute","Acircumflex","Atilde","Adieresis","Aring","AE","Ccedilla","Egrave","Eacute","Ecircumflex","Edieresis","Igrave","Iacute","Icircumflex","Idieresis","Eth","Ntilde","Ograve","Oacute","Ocircumflex","Otilde","Odieresis","multiply","Oslash","Ugrave","Uacute","Ucircumflex","Udieresis","Yacute","Thorn","germandbls","agrave","aacute","acircumflex","atilde","adieresis","aring","ae","ccedilla","egrave","eacute","ecircumflex","edieresis","igrave","iacute","icircumflex","idieresis","eth","ntilde","ograve","oacute","ocircumflex","otilde","odieresis","divide","oslash","ugrave","uacute","ucircumflex","udieresis","yacute","thorn","ydieresis"],Di=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","universal","numbersign","existential","percent","ampersand","suchthat","parenleft","parenright","asteriskmath","plus","comma","minus","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","congruent","Alpha","Beta","Chi","Delta","Epsilon","Phi","Gamma","Eta","Iota","theta1","Kappa","Lambda","Mu","Nu","Omicron","Pi","Theta","Rho","Sigma","Tau","Upsilon","sigma1","Omega","Xi","Psi","Zeta","bracketleft","therefore","bracketright","perpendicular","underscore","radicalex","alpha","beta","chi","delta","epsilon","phi","gamma","eta","iota","phi1","kappa","lambda","mu","nu","omicron","pi","theta","rho","sigma","tau","upsilon","omega1","omega","xi","psi","zeta","braceleft","bar","braceright","similar","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Euro","Upsilon1","minute","lessequal","fraction","infinity","florin","club","diamond","heart","spade","arrowboth","arrowleft","arrowup","arrowright","arrowdown","degree","plusminus","second","greaterequal","multiply","proportional","partialdiff","bullet","divide","notequal","equivalence","approxequal","ellipsis","arrowvertex","arrowhorizex","carriagereturn","aleph","Ifraktur","Rfraktur","weierstrass","circlemultiply","circleplus","emptyset","intersection","union","propersuperset","reflexsuperset","notsubset","propersubset","reflexsubset","element","notelement","angle","gradient","registerserif","copyrightserif","trademarkserif","product","radical","dotmath","logicalnot","logicaland","logicalor","arrowdblboth","arrowdblleft","arrowdblup","arrowdblright","arrowdbldown","lozenge","angleleft","registersans","copyrightsans","trademarksans","summation","parenlefttp","parenleftex","parenleftbt","bracketlefttp","bracketleftex","bracketleftbt","bracelefttp","braceleftmid","braceleftbt","braceex","","angleright","integral","integraltp","integralex","integralbt","parenrighttp","parenrightex","parenrightbt","bracketrighttp","bracketrightex","bracketrightbt","bracerighttp","bracerightmid","bracerightbt",""],bi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","a1","a2","a202","a3","a4","a5","a119","a118","a117","a11","a12","a13","a14","a15","a16","a105","a17","a18","a19","a20","a21","a22","a23","a24","a25","a26","a27","a28","a6","a7","a8","a9","a10","a29","a30","a31","a32","a33","a34","a35","a36","a37","a38","a39","a40","a41","a42","a43","a44","a45","a46","a47","a48","a49","a50","a51","a52","a53","a54","a55","a56","a57","a58","a59","a60","a61","a62","a63","a64","a65","a66","a67","a68","a69","a70","a71","a72","a73","a74","a203","a75","a204","a76","a77","a78","a79","a81","a82","a83","a84","a97","a98","a99","a100","","a89","a90","a93","a94","a91","a92","a205","a85","a206","a86","a87","a88","a95","a96","","","","","","","","","","","","","","","","","","","","a101","a102","a103","a104","a106","a107","a108","a112","a111","a110","a109","a120","a121","a122","a123","a124","a125","a126","a127","a128","a129","a130","a131","a132","a133","a134","a135","a136","a137","a138","a139","a140","a141","a142","a143","a144","a145","a146","a147","a148","a149","a150","a151","a152","a153","a154","a155","a156","a157","a158","a159","a160","a161","a163","a164","a196","a165","a192","a166","a167","a168","a169","a170","a171","a172","a173","a162","a174","a175","a176","a177","a178","a179","a193","a180","a199","a181","a200","a182","","a201","a183","a184","a197","a185","a194","a198","a186","a195","a187","a188","a189","a190","a191",""];function getEncoding(e){switch(e){case"WinAnsiEncoding":return wi;case"StandardEncoding":return yi;case"MacRomanEncoding":return mi;case"SymbolSetEncoding":return Di;case"ZapfDingbatsEncoding":return bi;case"ExpertEncoding":return fi;case"MacExpertEncoding":return pi;default:return null}}const Fi=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],Si=391,ki=[null,{id:"hstem",min:2,stackClearing:!0,stem:!0},null,{id:"vstem",min:2,stackClearing:!0,stem:!0},{id:"vmoveto",min:1,stackClearing:!0},{id:"rlineto",min:2,resetStack:!0},{id:"hlineto",min:1,resetStack:!0},{id:"vlineto",min:1,resetStack:!0},{id:"rrcurveto",min:6,resetStack:!0},null,{id:"callsubr",min:1,undefStack:!0},{id:"return",min:0,undefStack:!0},null,null,{id:"endchar",min:0,stackClearing:!0},null,null,null,{id:"hstemhm",min:2,stackClearing:!0,stem:!0},{id:"hintmask",min:0,stackClearing:!0},{id:"cntrmask",min:0,stackClearing:!0},{id:"rmoveto",min:2,stackClearing:!0},{id:"hmoveto",min:1,stackClearing:!0},{id:"vstemhm",min:2,stackClearing:!0,stem:!0},{id:"rcurveline",min:8,resetStack:!0},{id:"rlinecurve",min:8,resetStack:!0},{id:"vvcurveto",min:4,resetStack:!0},{id:"hhcurveto",min:4,resetStack:!0},null,{id:"callgsubr",min:1,undefStack:!0},{id:"vhcurveto",min:4,resetStack:!0},{id:"hvcurveto",min:4,resetStack:!0}],Ri=[null,null,null,{id:"and",min:2,stackDelta:-1},{id:"or",min:2,stackDelta:-1},{id:"not",min:1,stackDelta:0},null,null,null,{id:"abs",min:1,stackDelta:0},{id:"add",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]+e[t-1]}},{id:"sub",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]-e[t-1]}},{id:"div",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]/e[t-1]}},null,{id:"neg",min:1,stackDelta:0,stackFn(e,t){e[t-1]=-e[t-1]}},{id:"eq",min:2,stackDelta:-1},null,null,{id:"drop",min:1,stackDelta:-1},null,{id:"put",min:2,stackDelta:-2},{id:"get",min:1,stackDelta:0},{id:"ifelse",min:4,stackDelta:-3},{id:"random",min:0,stackDelta:1},{id:"mul",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]*e[t-1]}},null,{id:"sqrt",min:1,stackDelta:0},{id:"dup",min:1,stackDelta:1},{id:"exch",min:2,stackDelta:0},{id:"index",min:2,stackDelta:0},{id:"roll",min:3,stackDelta:-2},null,null,null,{id:"hflex",min:7,resetStack:!0},{id:"flex",min:13,resetStack:!0},{id:"hflex1",min:9,resetStack:!0},{id:"flex1",min:11,resetStack:!0}];class CFFParser{constructor(e,t,i){this.bytes=e.getBytes();this.properties=t;this.seacAnalysisEnabled=!!i}parse(){const e=this.properties,t=new CFF;this.cff=t;const i=this.parseHeader(),a=this.parseIndex(i.endPos),s=this.parseIndex(a.endPos),r=this.parseIndex(s.endPos),n=this.parseIndex(r.endPos),g=this.parseDict(s.obj.get(0)),o=this.createDict(CFFTopDict,g,t.strings);t.header=i.obj;t.names=this.parseNameIndex(a.obj);t.strings=this.parseStringIndex(r.obj);t.topDict=o;t.globalSubrIndex=n.obj;this.parsePrivateDict(t.topDict);t.isCIDFont=o.hasName("ROS");const c=o.getByName("CharStrings"),C=this.parseIndex(c).obj,h=o.getByName("FontMatrix");h&&(e.fontMatrix=h);const l=o.getByName("FontBBox");if(l){e.ascent=Math.max(l[3],l[1]);e.descent=Math.min(l[1],l[3]);e.ascentScaled=!0}let Q,E;if(t.isCIDFont){const e=this.parseIndex(o.getByName("FDArray")).obj;for(let i=0,a=e.count;i<a;++i){const a=e.get(i),s=this.createDict(CFFTopDict,this.parseDict(a),t.strings);this.parsePrivateDict(s);t.fdArray.push(s)}E=null;Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!0);t.fdSelect=this.parseFDSelect(o.getByName("FDSelect"),C.count)}else{Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!1);E=this.parseEncoding(o.getByName("Encoding"),e,t.strings,Q.charset)}t.charset=Q;t.encoding=E;const u=this.parseCharStrings({charStrings:C,localSubrIndex:o.privateDict.subrsIndex,globalSubrIndex:n.obj,fdSelect:t.fdSelect,fdArray:t.fdArray,privateDict:o.privateDict});t.charStrings=u.charStrings;t.seacs=u.seacs;t.widths=u.widths;return t}parseHeader(){let e=this.bytes;const t=e.length;let i=0;for(;i<t&&1!==e[i];)++i;if(i>=t)throw new FormatError("Invalid CFF header");if(0!==i){info("cff data is shifted");e=e.subarray(i);this.bytes=e}const a=e[0],s=e[1],r=e[2],n=e[3];return{obj:new CFFHeader(a,s,r,n),endPos:r}}parseDict(e){let t=0;function parseOperand(){let i=e[t++];if(30===i)return function parseFloatOperand(){let i="";const a=15,s=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"],r=e.length;for(;t<r;){const r=e[t++],n=r>>4,g=15&r;if(n===a)break;i+=s[n];if(g===a)break;i+=s[g]}return parseFloat(i)}();if(28===i){i=e[t++];i=(i<<24|e[t++]<<16)>>16;return i}if(29===i){i=e[t++];i=i<<8|e[t++];i=i<<8|e[t++];i=i<<8|e[t++];return i}if(i>=32&&i<=246)return i-139;if(i>=247&&i<=250)return 256*(i-247)+e[t++]+108;if(i>=251&&i<=254)return-256*(i-251)-e[t++]-108;warn('CFFParser_parseDict: "'+i+'" is a reserved command.');return NaN}let i=[];const a=[];t=0;const s=e.length;for(;t<s;){let s=e[t];if(s<=21){12===s&&(s=s<<8|e[++t]);a.push([s,i]);i=[];++t}else i.push(parseOperand())}return a}parseIndex(e){const t=new CFFIndex,i=this.bytes,a=i[e++]<<8|i[e++],s=[];let r,n,g=e;if(0!==a){const t=i[e++],o=e+(a+1)*t-1;for(r=0,n=a+1;r<n;++r){let a=0;for(let s=0;s<t;++s){a<<=8;a+=i[e++]}s.push(o+a)}g=s[a]}for(r=0,n=s.length-1;r<n;++r){const e=s[r],a=s[r+1];t.add(i.subarray(e,a))}return{obj:t,endPos:g}}parseNameIndex(e){const t=[];for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.push(bytesToString(a))}return t}parseStringIndex(e){const t=new CFFStrings;for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.add(bytesToString(a))}return t}createDict(e,t,i){const a=new e(i);for(const[e,i]of t)a.setByKey(e,i);return a}parseCharString(e,t,i,a){if(!t||e.callDepth>10)return!1;let s=e.stackSize;const r=e.stack;let n=t.length;for(let g=0;g<n;){const o=t[g++];let c=null;if(12===o){const e=t[g++];if(0===e){t[g-2]=139;t[g-1]=22;s=0}else c=Ri[e]}else if(28===o){r[s]=(t[g]<<24|t[g+1]<<16)>>16;g+=2;s++}else if(14===o){if(s>=4){s-=4;if(this.seacAnalysisEnabled){e.seac=r.slice(s,s+4);return!1}}c=ki[o]}else if(o>=32&&o<=246){r[s]=o-139;s++}else if(o>=247&&o<=254){r[s]=o<251?(o-247<<8)+t[g]+108:-(o-251<<8)-t[g]-108;g++;s++}else if(255===o){r[s]=(t[g]<<24|t[g+1]<<16|t[g+2]<<8|t[g+3])/65536;g+=4;s++}else if(19===o||20===o){e.hints+=s>>1;if(0===e.hints){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}g+=e.hints+7>>3;s%=2;c=ki[o]}else{if(10===o||29===o){const t=10===o?i:a;if(!t){c=ki[o];warn("Missing subrsIndex for "+c.id);return!1}let n=32768;t.count<1240?n=107:t.count<33900&&(n=1131);const g=r[--s]+n;if(g<0||g>=t.count||isNaN(g)){c=ki[o];warn("Out of bounds subrIndex for "+c.id);return!1}e.stackSize=s;e.callDepth++;if(!this.parseCharString(e,t.get(g),i,a))return!1;e.callDepth--;s=e.stackSize;continue}if(11===o){e.stackSize=s;return!0}if(0===o&&g===t.length){t[g-1]=14;c=ki[14]}else{if(9===o){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}c=ki[o]}}if(c){if(c.stem){e.hints+=s>>1;if(3===o||23===o)e.hasVStems=!0;else if(e.hasVStems&&(1===o||18===o)){warn("CFF stem hints are in wrong order");t[g-1]=1===o?3:23}}if("min"in c&&!e.undefStack&&s<c.min){warn("Not enough parameters for "+c.id+"; actual: "+s+", expected: "+c.min);if(0===s){t[g-1]=14;return!0}return!1}if(e.firstStackClearing&&c.stackClearing){e.firstStackClearing=!1;s-=c.min;s>=2&&c.stem?s%=2:s>1&&warn("Found too many parameters for stack-clearing command");s>0&&(e.width=r[s-1])}if("stackDelta"in c){"stackFn"in c&&c.stackFn(r,s);s+=c.stackDelta}else if(c.stackClearing)s=0;else if(c.resetStack){s=0;e.undefStack=!1}else if(c.undefStack){s=0;e.undefStack=!0;e.firstStackClearing=!1}}}n<t.length&&t.fill(14,n);e.stackSize=s;return!0}parseCharStrings({charStrings:e,localSubrIndex:t,globalSubrIndex:i,fdSelect:a,fdArray:s,privateDict:r}){const n=[],g=[],o=e.count;for(let c=0;c<o;c++){const o=e.get(c),C={callDepth:0,stackSize:0,stack:[],undefStack:!0,hints:0,firstStackClearing:!0,seac:null,width:null,hasVStems:!1};let h=!0,l=null,Q=r;if(a&&s.length){const e=a.getFDIndex(c);if(-1===e){warn("Glyph index is not in fd select.");h=!1}if(e>=s.length){warn("Invalid fd index for glyph index.");h=!1}if(h){Q=s[e].privateDict;l=Q.subrsIndex}}else t&&(l=t);h&&(h=this.parseCharString(C,o,l,i));if(null!==C.width){const e=Q.getByName("nominalWidthX");g[c]=e+C.width}else{const e=Q.getByName("defaultWidthX");g[c]=e}null!==C.seac&&(n[c]=C.seac);h||e.set(c,new Uint8Array([14]))}return{charStrings:e,seacs:n,widths:g}}emptyPrivateDictionary(e){const t=this.createDict(CFFPrivateDict,[],e.strings);e.setByKey(18,[0,0]);e.privateDict=t}parsePrivateDict(e){if(!e.hasName("Private")){this.emptyPrivateDictionary(e);return}const t=e.getByName("Private");if(!Array.isArray(t)||2!==t.length){e.removeByName("Private");return}const i=t[0],a=t[1];if(0===i||a>=this.bytes.length){this.emptyPrivateDictionary(e);return}const s=a+i,r=this.bytes.subarray(a,s),n=this.parseDict(r),g=this.createDict(CFFPrivateDict,n,e.strings);e.privateDict=g;0===g.getByName("ExpansionFactor")&&g.setByName("ExpansionFactor",.06);if(!g.getByName("Subrs"))return;const o=g.getByName("Subrs"),c=a+o;if(0===o||c>=this.bytes.length){this.emptyPrivateDictionary(e);return}const C=this.parseIndex(c);g.subrsIndex=C.obj}parseCharsets(e,t,i,a){if(0===e)return new CFFCharset(!0,xi.ISO_ADOBE,Ei);if(1===e)return new CFFCharset(!0,xi.EXPERT,ui);if(2===e)return new CFFCharset(!0,xi.EXPERT_SUBSET,di);const s=this.bytes,r=e,n=s[e++],g=[a?0:".notdef"];let o,c,C;t-=1;switch(n){case 0:for(C=0;C<t;C++){o=s[e++]<<8|s[e++];g.push(a?o:i.get(o))}break;case 1:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;case 2:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++]<<8|s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;default:throw new FormatError("Unknown charset format")}const h=e,l=s.subarray(r,h);return new CFFCharset(!1,n,g,l)}parseEncoding(e,t,i,a){const s=Object.create(null),r=this.bytes;let n,g,o,c=!1,C=null;if(0===e||1===e){c=!0;n=e;const t=e?fi:yi;for(g=0,o=a.length;g<o;g++){const e=t.indexOf(a[g]);-1!==e&&(s[e]=g)}}else{const t=e;n=r[e++];switch(127&n){case 0:const t=r[e++];for(g=1;g<=t;g++)s[r[e++]]=g;break;case 1:const i=r[e++];let a=1;for(g=0;g<i;g++){const t=r[e++],i=r[e++];for(let e=t;e<=t+i;e++)s[e]=a++}break;default:throw new FormatError(`Unknown encoding format: ${n} in CFF`)}const o=e;if(128&n){r[t]&=127;!function readSupplement(){const t=r[e++];for(g=0;g<t;g++){const t=r[e++],n=(r[e++]<<8)+(255&r[e++]);s[t]=a.indexOf(i.get(n))}}()}C=r.subarray(t,o)}n&=127;return new CFFEncoding(c,n,s,C)}parseFDSelect(e,t){const i=this.bytes,a=i[e++],s=[];let r;switch(a){case 0:for(r=0;r<t;++r){const t=i[e++];s.push(t)}break;case 3:const n=i[e++]<<8|i[e++];for(r=0;r<n;++r){let t=i[e++]<<8|i[e++];if(0===r&&0!==t){warn("parseFDSelect: The first range must have a first GID of 0 -- trying to recover.");t=0}const a=i[e++],n=i[e]<<8|i[e+1];for(let e=t;e<n;++e)s.push(a)}e+=2;break;default:throw new FormatError(`parseFDSelect: Unknown format "${a}".`)}if(s.length!==t)throw new FormatError("parseFDSelect: Invalid font data.");return new CFFFDSelect(a,s)}}class CFF{constructor(){this.header=null;this.names=[];this.topDict=null;this.strings=new CFFStrings;this.globalSubrIndex=null;this.encoding=null;this.charset=null;this.charStrings=null;this.fdArray=[];this.fdSelect=null;this.isCIDFont=!1}duplicateFirstGlyph(){if(this.charStrings.count>=65535){warn("Not enough space in charstrings to duplicate first glyph.");return}const e=this.charStrings.get(0);this.charStrings.add(e);this.isCIDFont&&this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0])}hasGlyphId(e){if(e<0||e>=this.charStrings.count)return!1;return this.charStrings.get(e).length>0}}class CFFHeader{constructor(e,t,i,a){this.major=e;this.minor=t;this.hdrSize=i;this.offSize=a}}class CFFStrings{constructor(){this.strings=[]}get(e){return e>=0&&e<=390?Fi[e]:e-Si<=this.strings.length?this.strings[e-Si]:Fi[0]}getSID(e){let t=Fi.indexOf(e);if(-1!==t)return t;t=this.strings.indexOf(e);return-1!==t?t+Si:-1}add(e){this.strings.push(e)}get count(){return this.strings.length}}class CFFIndex{constructor(){this.objects=[];this.length=0}add(e){this.length+=e.length;this.objects.push(e)}set(e,t){this.length+=t.length-this.objects[e].length;this.objects[e]=t}get(e){return this.objects[e]}get count(){return this.objects.length}}class CFFDict{constructor(e,t){this.keyToNameMap=e.keyToNameMap;this.nameToKeyMap=e.nameToKeyMap;this.defaults=e.defaults;this.types=e.types;this.opcodes=e.opcodes;this.order=e.order;this.strings=t;this.values=Object.create(null)}setByKey(e,t){if(!(e in this.keyToNameMap))return!1;if(0===t.length)return!0;for(const i of t)if(isNaN(i)){warn(`Invalid CFFDict value: "${t}" for key "${e}".`);return!0}const i=this.types[e];"num"!==i&&"sid"!==i&&"offset"!==i||(t=t[0]);this.values[e]=t;return!0}setByName(e,t){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name "${e}"`);this.values[this.nameToKeyMap[e]]=t}hasName(e){return this.nameToKeyMap[e]in this.values}getByName(e){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name ${e}"`);const t=this.nameToKeyMap[e];return t in this.values?this.values[t]:this.defaults[t]}removeByName(e){delete this.values[this.nameToKeyMap[e]]}static createTables(e){const t={keyToNameMap:{},nameToKeyMap:{},defaults:{},types:{},opcodes:{},order:[]};for(const i of e){const e=Array.isArray(i[0])?(i[0][0]<<8)+i[0][1]:i[0];t.keyToNameMap[e]=i[1];t.nameToKeyMap[i[1]]=e;t.types[e]=i[2];t.defaults[e]=i[3];t.opcodes[e]=Array.isArray(i[0])?i[0]:[i[0]];t.order.push(e)}return t}}const Ni=[[[12,30],"ROS",["sid","sid","num"],null],[[12,20],"SyntheticBase","num",null],[0,"version","sid",null],[1,"Notice","sid",null],[[12,0],"Copyright","sid",null],[2,"FullName","sid",null],[3,"FamilyName","sid",null],[4,"Weight","sid",null],[[12,1],"isFixedPitch","num",0],[[12,2],"ItalicAngle","num",0],[[12,3],"UnderlinePosition","num",-100],[[12,4],"UnderlineThickness","num",50],[[12,5],"PaintType","num",0],[[12,6],"CharstringType","num",2],[[12,7],"FontMatrix",["num","num","num","num","num","num"],[.001,0,0,.001,0,0]],[13,"UniqueID","num",null],[5,"FontBBox",["num","num","num","num"],[0,0,0,0]],[[12,8],"StrokeWidth","num",0],[14,"XUID","array",null],[15,"charset","offset",0],[16,"Encoding","offset",0],[17,"CharStrings","offset",0],[18,"Private",["offset","offset"],null],[[12,21],"PostScript","sid",null],[[12,22],"BaseFontName","sid",null],[[12,23],"BaseFontBlend","delta",null],[[12,31],"CIDFontVersion","num",0],[[12,32],"CIDFontRevision","num",0],[[12,33],"CIDFontType","num",0],[[12,34],"CIDCount","num",8720],[[12,35],"UIDBase","num",null],[[12,37],"FDSelect","offset",null],[[12,36],"FDArray","offset",null],[[12,38],"FontName","sid",null]];class CFFTopDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Ni))}constructor(e){super(CFFTopDict.tables,e);this.privateDict=null}}const Gi=[[6,"BlueValues","delta",null],[7,"OtherBlues","delta",null],[8,"FamilyBlues","delta",null],[9,"FamilyOtherBlues","delta",null],[[12,9],"BlueScale","num",.039625],[[12,10],"BlueShift","num",7],[[12,11],"BlueFuzz","num",1],[10,"StdHW","num",null],[11,"StdVW","num",null],[[12,12],"StemSnapH","delta",null],[[12,13],"StemSnapV","delta",null],[[12,14],"ForceBold","num",0],[[12,17],"LanguageGroup","num",0],[[12,18],"ExpansionFactor","num",.06],[[12,19],"initialRandomSeed","num",0],[20,"defaultWidthX","num",0],[21,"nominalWidthX","num",0],[19,"Subrs","offset",null]];class CFFPrivateDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Gi))}constructor(e){super(CFFPrivateDict.tables,e);this.subrsIndex=null}}const xi={ISO_ADOBE:0,EXPERT:1,EXPERT_SUBSET:2};class CFFCharset{constructor(e,t,i,a){this.predefined=e;this.format=t;this.charset=i;this.raw=a}}class CFFEncoding{constructor(e,t,i,a){this.predefined=e;this.format=t;this.encoding=i;this.raw=a}}class CFFFDSelect{constructor(e,t){this.format=e;this.fdSelect=t}getFDIndex(e){return e<0||e>=this.fdSelect.length?-1:this.fdSelect[e]}}class CFFOffsetTracker{constructor(){this.offsets=Object.create(null)}isTracking(e){return e in this.offsets}track(e,t){if(e in this.offsets)throw new FormatError(`Already tracking location of ${e}`);this.offsets[e]=t}offset(e){for(const t in this.offsets)this.offsets[t]+=e}setEntryLocation(e,t,i){if(!(e in this.offsets))throw new FormatError(`Not tracking location of ${e}`);const a=i.data,s=this.offsets[e];for(let e=0,i=t.length;e<i;++e){const i=5*e+s,r=i+1,n=i+2,g=i+3,o=i+4;if(29!==a[i]||0!==a[r]||0!==a[n]||0!==a[g]||0!==a[o])throw new FormatError("writing to an offset that is not empty");const c=t[e];a[i]=29;a[r]=c>>24&255;a[n]=c>>16&255;a[g]=c>>8&255;a[o]=255&c}}}class CFFCompiler{constructor(e){this.cff=e}compile(){const e=this.cff,t={data:[],length:0,add(e){try{this.data.push(...e)}catch{this.data=this.data.concat(e)}this.length=this.data.length}},i=this.compileHeader(e.header);t.add(i);const a=this.compileNameIndex(e.names);t.add(a);if(e.isCIDFont&&e.topDict.hasName("FontMatrix")){const t=e.topDict.getByName("FontMatrix");e.topDict.removeByName("FontMatrix");for(const i of e.fdArray){let e=t.slice(0);i.hasName("FontMatrix")&&(e=Util.transform(e,i.getByName("FontMatrix")));i.setByName("FontMatrix",e)}}const s=e.topDict.getByName("XUID");s?.length>16&&e.topDict.removeByName("XUID");e.topDict.setByName("charset",0);let r=this.compileTopDicts([e.topDict],t.length,e.isCIDFont);t.add(r.output);const n=r.trackers[0],g=this.compileStringIndex(e.strings.strings);t.add(g);const o=this.compileIndex(e.globalSubrIndex);t.add(o);if(e.encoding&&e.topDict.hasName("Encoding"))if(e.encoding.predefined)n.setEntryLocation("Encoding",[e.encoding.format],t);else{const i=this.compileEncoding(e.encoding);n.setEntryLocation("Encoding",[t.length],t);t.add(i)}const c=this.compileCharset(e.charset,e.charStrings.count,e.strings,e.isCIDFont);n.setEntryLocation("charset",[t.length],t);t.add(c);const C=this.compileCharStrings(e.charStrings);n.setEntryLocation("CharStrings",[t.length],t);t.add(C);if(e.isCIDFont){n.setEntryLocation("FDSelect",[t.length],t);const i=this.compileFDSelect(e.fdSelect);t.add(i);r=this.compileTopDicts(e.fdArray,t.length,!0);n.setEntryLocation("FDArray",[t.length],t);t.add(r.output);const a=r.trackers;this.compilePrivateDicts(e.fdArray,a,t)}this.compilePrivateDicts([e.topDict],[n],t);t.add([0]);return t.data}encodeNumber(e){return Number.isInteger(e)?this.encodeInteger(e):this.encodeFloat(e)}static get EncodeFloatRegExp(){return shadow(this,"EncodeFloatRegExp",/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/)}encodeFloat(e){let t=e.toString();const i=CFFCompiler.EncodeFloatRegExp.exec(t);if(i){const a=parseFloat("1e"+((i[2]?+i[2]:0)+i[1].length));t=(Math.round(e*a)/a).toString()}let a,s,r="";for(a=0,s=t.length;a<s;++a){const e=t[a];r+="e"===e?"-"===t[++a]?"c":"b":"."===e?"a":"-"===e?"e":e}r+=1&r.length?"f":"ff";const n=[30];for(a=0,s=r.length;a<s;a+=2)n.push(parseInt(r.substring(a,a+2),16));return n}encodeInteger(e){let t;t=e>=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?[28,e>>8&255,255&e]:[29,e>>24&255,e>>16&255,e>>8&255,255&e];return t}compileHeader(e){return[e.major,e.minor,4,e.offSize]}compileNameIndex(e){const t=new CFFIndex;for(const i of e){const e=Math.min(i.length,127);let a=new Array(e);for(let t=0;t<e;t++){let e=i[t];(e<"!"||e>"~"||"["===e||"]"===e||"("===e||")"===e||"{"===e||"}"===e||"<"===e||">"===e||"/"===e||"%"===e)&&(e="_");a[t]=e}a=a.join("");""===a&&(a="Bad_Font_Name");t.add(stringToBytes(a))}return this.compileIndex(t)}compileTopDicts(e,t,i){const a=[];let s=new CFFIndex;for(const r of e){if(i){r.removeByName("CIDFontVersion");r.removeByName("CIDFontRevision");r.removeByName("CIDFontType");r.removeByName("CIDCount");r.removeByName("UIDBase")}const e=new CFFOffsetTracker,n=this.compileDict(r,e);a.push(e);s.add(n);e.offset(t)}s=this.compileIndex(s,a);return{trackers:a,output:s}}compilePrivateDicts(e,t,i){for(let a=0,s=e.length;a<s;++a){const s=e[a],r=s.privateDict;if(!r||!s.hasName("Private"))throw new FormatError("There must be a private dictionary.");const n=new CFFOffsetTracker,g=this.compileDict(r,n);let o=i.length;n.offset(o);g.length||(o=0);t[a].setEntryLocation("Private",[g.length,o],i);i.add(g);if(r.subrsIndex&&r.hasName("Subrs")){const e=this.compileIndex(r.subrsIndex);n.setEntryLocation("Subrs",[g.length],i);i.add(e)}}}compileDict(e,t){const i=[];for(const a of e.order){if(!(a in e.values))continue;let s=e.values[a],r=e.types[a];Array.isArray(r)||(r=[r]);Array.isArray(s)||(s=[s]);if(0!==s.length){for(let n=0,g=r.length;n<g;++n){const g=r[n],o=s[n];switch(g){case"num":case"sid":i.push(...this.encodeNumber(o));break;case"offset":const r=e.keyToNameMap[a];t.isTracking(r)||t.track(r,i.length);i.push(29,0,0,0,0);break;case"array":case"delta":i.push(...this.encodeNumber(o));for(let e=1,t=s.length;e<t;++e)i.push(...this.encodeNumber(s[e]));break;default:throw new FormatError(`Unknown data type of ${g}`)}}i.push(...e.opcodes[a])}}return i}compileStringIndex(e){const t=new CFFIndex;for(const i of e)t.add(stringToBytes(i));return this.compileIndex(t)}compileCharStrings(e){const t=new CFFIndex;for(let i=0;i<e.count;i++){const a=e.get(i);0!==a.length?t.add(a):t.add(new Uint8Array([139,14]))}return this.compileIndex(t)}compileCharset(e,t,i,a){let s;const r=t-1;if(a)s=new Uint8Array([2,0,0,r>>8&255,255&r]);else{s=new Uint8Array(1+2*r);s[0]=0;let t=0;const a=e.charset.length;let n=!1;for(let r=1;r<s.length;r+=2){let g=0;if(t<a){const a=e.charset[t++];g=i.getSID(a);if(-1===g){g=0;if(!n){n=!0;warn(`Couldn't find ${a} in CFF strings`)}}}s[r]=g>>8&255;s[r+1]=255&g}}return this.compileTypedArray(s)}compileEncoding(e){return this.compileTypedArray(e.raw)}compileFDSelect(e){const t=e.format;let i,a;switch(t){case 0:i=new Uint8Array(1+e.fdSelect.length);i[0]=t;for(a=0;a<e.fdSelect.length;a++)i[a+1]=e.fdSelect[a];break;case 3:const s=0;let r=e.fdSelect[0];const n=[t,0,0,s>>8&255,255&s,r];for(a=1;a<e.fdSelect.length;a++){const t=e.fdSelect[a];if(t!==r){n.push(a>>8&255,255&a,t);r=t}}const g=(n.length-3)/3;n[1]=g>>8&255;n[2]=255&g;n.push(a>>8&255,255&a);i=new Uint8Array(n)}return this.compileTypedArray(i)}compileTypedArray(e){return Array.from(e)}compileIndex(e,t=[]){const i=e.objects,a=i.length;if(0===a)return[0,0];const s=[a>>8&255,255&a];let r,n,g=1;for(r=0;r<a;++r)g+=i[r].length;n=g<256?1:g<65536?2:g<16777216?3:4;s.push(n);let o=1;for(r=0;r<a+1;r++){1===n?s.push(255&o):2===n?s.push(o>>8&255,255&o):3===n?s.push(o>>16&255,o>>8&255,255&o):s.push(o>>>24&255,o>>16&255,o>>8&255,255&o);i[r]&&(o+=i[r].length)}for(r=0;r<a;r++){t[r]&&t[r].offset(s.length);s.push(...i[r])}return s}}const Mi=getLookupTableFactory((function(e){e.A=65;e.AE=198;e.AEacute=508;e.AEmacron=482;e.AEsmall=63462;e.Aacute=193;e.Aacutesmall=63457;e.Abreve=258;e.Abreveacute=7854;e.Abrevecyrillic=1232;e.Abrevedotbelow=7862;e.Abrevegrave=7856;e.Abrevehookabove=7858;e.Abrevetilde=7860;e.Acaron=461;e.Acircle=9398;e.Acircumflex=194;e.Acircumflexacute=7844;e.Acircumflexdotbelow=7852;e.Acircumflexgrave=7846;e.Acircumflexhookabove=7848;e.Acircumflexsmall=63458;e.Acircumflextilde=7850;e.Acute=63177;e.Acutesmall=63412;e.Acyrillic=1040;e.Adblgrave=512;e.Adieresis=196;e.Adieresiscyrillic=1234;e.Adieresismacron=478;e.Adieresissmall=63460;e.Adotbelow=7840;e.Adotmacron=480;e.Agrave=192;e.Agravesmall=63456;e.Ahookabove=7842;e.Aiecyrillic=1236;e.Ainvertedbreve=514;e.Alpha=913;e.Alphatonos=902;e.Amacron=256;e.Amonospace=65313;e.Aogonek=260;e.Aring=197;e.Aringacute=506;e.Aringbelow=7680;e.Aringsmall=63461;e.Asmall=63329;e.Atilde=195;e.Atildesmall=63459;e.Aybarmenian=1329;e.B=66;e.Bcircle=9399;e.Bdotaccent=7682;e.Bdotbelow=7684;e.Becyrillic=1041;e.Benarmenian=1330;e.Beta=914;e.Bhook=385;e.Blinebelow=7686;e.Bmonospace=65314;e.Brevesmall=63220;e.Bsmall=63330;e.Btopbar=386;e.C=67;e.Caarmenian=1342;e.Cacute=262;e.Caron=63178;e.Caronsmall=63221;e.Ccaron=268;e.Ccedilla=199;e.Ccedillaacute=7688;e.Ccedillasmall=63463;e.Ccircle=9400;e.Ccircumflex=264;e.Cdot=266;e.Cdotaccent=266;e.Cedillasmall=63416;e.Chaarmenian=1353;e.Cheabkhasiancyrillic=1212;e.Checyrillic=1063;e.Chedescenderabkhasiancyrillic=1214;e.Chedescendercyrillic=1206;e.Chedieresiscyrillic=1268;e.Cheharmenian=1347;e.Chekhakassiancyrillic=1227;e.Cheverticalstrokecyrillic=1208;e.Chi=935;e.Chook=391;e.Circumflexsmall=63222;e.Cmonospace=65315;e.Coarmenian=1361;e.Csmall=63331;e.D=68;e.DZ=497;e.DZcaron=452;e.Daarmenian=1332;e.Dafrican=393;e.Dcaron=270;e.Dcedilla=7696;e.Dcircle=9401;e.Dcircumflexbelow=7698;e.Dcroat=272;e.Ddotaccent=7690;e.Ddotbelow=7692;e.Decyrillic=1044;e.Deicoptic=1006;e.Delta=8710;e.Deltagreek=916;e.Dhook=394;e.Dieresis=63179;e.DieresisAcute=63180;e.DieresisGrave=63181;e.Dieresissmall=63400;e.Digammagreek=988;e.Djecyrillic=1026;e.Dlinebelow=7694;e.Dmonospace=65316;e.Dotaccentsmall=63223;e.Dslash=272;e.Dsmall=63332;e.Dtopbar=395;e.Dz=498;e.Dzcaron=453;e.Dzeabkhasiancyrillic=1248;e.Dzecyrillic=1029;e.Dzhecyrillic=1039;e.E=69;e.Eacute=201;e.Eacutesmall=63465;e.Ebreve=276;e.Ecaron=282;e.Ecedillabreve=7708;e.Echarmenian=1333;e.Ecircle=9402;e.Ecircumflex=202;e.Ecircumflexacute=7870;e.Ecircumflexbelow=7704;e.Ecircumflexdotbelow=7878;e.Ecircumflexgrave=7872;e.Ecircumflexhookabove=7874;e.Ecircumflexsmall=63466;e.Ecircumflextilde=7876;e.Ecyrillic=1028;e.Edblgrave=516;e.Edieresis=203;e.Edieresissmall=63467;e.Edot=278;e.Edotaccent=278;e.Edotbelow=7864;e.Efcyrillic=1060;e.Egrave=200;e.Egravesmall=63464;e.Eharmenian=1335;e.Ehookabove=7866;e.Eightroman=8551;e.Einvertedbreve=518;e.Eiotifiedcyrillic=1124;e.Elcyrillic=1051;e.Elevenroman=8554;e.Emacron=274;e.Emacronacute=7702;e.Emacrongrave=7700;e.Emcyrillic=1052;e.Emonospace=65317;e.Encyrillic=1053;e.Endescendercyrillic=1186;e.Eng=330;e.Enghecyrillic=1188;e.Enhookcyrillic=1223;e.Eogonek=280;e.Eopen=400;e.Epsilon=917;e.Epsilontonos=904;e.Ercyrillic=1056;e.Ereversed=398;e.Ereversedcyrillic=1069;e.Escyrillic=1057;e.Esdescendercyrillic=1194;e.Esh=425;e.Esmall=63333;e.Eta=919;e.Etarmenian=1336;e.Etatonos=905;e.Eth=208;e.Ethsmall=63472;e.Etilde=7868;e.Etildebelow=7706;e.Euro=8364;e.Ezh=439;e.Ezhcaron=494;e.Ezhreversed=440;e.F=70;e.Fcircle=9403;e.Fdotaccent=7710;e.Feharmenian=1366;e.Feicoptic=996;e.Fhook=401;e.Fitacyrillic=1138;e.Fiveroman=8548;e.Fmonospace=65318;e.Fourroman=8547;e.Fsmall=63334;e.G=71;e.GBsquare=13191;e.Gacute=500;e.Gamma=915;e.Gammaafrican=404;e.Gangiacoptic=1002;e.Gbreve=286;e.Gcaron=486;e.Gcedilla=290;e.Gcircle=9404;e.Gcircumflex=284;e.Gcommaaccent=290;e.Gdot=288;e.Gdotaccent=288;e.Gecyrillic=1043;e.Ghadarmenian=1346;e.Ghemiddlehookcyrillic=1172;e.Ghestrokecyrillic=1170;e.Gheupturncyrillic=1168;e.Ghook=403;e.Gimarmenian=1331;e.Gjecyrillic=1027;e.Gmacron=7712;e.Gmonospace=65319;e.Grave=63182;e.Gravesmall=63328;e.Gsmall=63335;e.Gsmallhook=667;e.Gstroke=484;e.H=72;e.H18533=9679;e.H18543=9642;e.H18551=9643;e.H22073=9633;e.HPsquare=13259;e.Haabkhasiancyrillic=1192;e.Hadescendercyrillic=1202;e.Hardsigncyrillic=1066;e.Hbar=294;e.Hbrevebelow=7722;e.Hcedilla=7720;e.Hcircle=9405;e.Hcircumflex=292;e.Hdieresis=7718;e.Hdotaccent=7714;e.Hdotbelow=7716;e.Hmonospace=65320;e.Hoarmenian=1344;e.Horicoptic=1e3;e.Hsmall=63336;e.Hungarumlaut=63183;e.Hungarumlautsmall=63224;e.Hzsquare=13200;e.I=73;e.IAcyrillic=1071;e.IJ=306;e.IUcyrillic=1070;e.Iacute=205;e.Iacutesmall=63469;e.Ibreve=300;e.Icaron=463;e.Icircle=9406;e.Icircumflex=206;e.Icircumflexsmall=63470;e.Icyrillic=1030;e.Idblgrave=520;e.Idieresis=207;e.Idieresisacute=7726;e.Idieresiscyrillic=1252;e.Idieresissmall=63471;e.Idot=304;e.Idotaccent=304;e.Idotbelow=7882;e.Iebrevecyrillic=1238;e.Iecyrillic=1045;e.Ifraktur=8465;e.Igrave=204;e.Igravesmall=63468;e.Ihookabove=7880;e.Iicyrillic=1048;e.Iinvertedbreve=522;e.Iishortcyrillic=1049;e.Imacron=298;e.Imacroncyrillic=1250;e.Imonospace=65321;e.Iniarmenian=1339;e.Iocyrillic=1025;e.Iogonek=302;e.Iota=921;e.Iotaafrican=406;e.Iotadieresis=938;e.Iotatonos=906;e.Ismall=63337;e.Istroke=407;e.Itilde=296;e.Itildebelow=7724;e.Izhitsacyrillic=1140;e.Izhitsadblgravecyrillic=1142;e.J=74;e.Jaarmenian=1345;e.Jcircle=9407;e.Jcircumflex=308;e.Jecyrillic=1032;e.Jheharmenian=1355;e.Jmonospace=65322;e.Jsmall=63338;e.K=75;e.KBsquare=13189;e.KKsquare=13261;e.Kabashkircyrillic=1184;e.Kacute=7728;e.Kacyrillic=1050;e.Kadescendercyrillic=1178;e.Kahookcyrillic=1219;e.Kappa=922;e.Kastrokecyrillic=1182;e.Kaverticalstrokecyrillic=1180;e.Kcaron=488;e.Kcedilla=310;e.Kcircle=9408;e.Kcommaaccent=310;e.Kdotbelow=7730;e.Keharmenian=1364;e.Kenarmenian=1343;e.Khacyrillic=1061;e.Kheicoptic=998;e.Khook=408;e.Kjecyrillic=1036;e.Klinebelow=7732;e.Kmonospace=65323;e.Koppacyrillic=1152;e.Koppagreek=990;e.Ksicyrillic=1134;e.Ksmall=63339;e.L=76;e.LJ=455;e.LL=63167;e.Lacute=313;e.Lambda=923;e.Lcaron=317;e.Lcedilla=315;e.Lcircle=9409;e.Lcircumflexbelow=7740;e.Lcommaaccent=315;e.Ldot=319;e.Ldotaccent=319;e.Ldotbelow=7734;e.Ldotbelowmacron=7736;e.Liwnarmenian=1340;e.Lj=456;e.Ljecyrillic=1033;e.Llinebelow=7738;e.Lmonospace=65324;e.Lslash=321;e.Lslashsmall=63225;e.Lsmall=63340;e.M=77;e.MBsquare=13190;e.Macron=63184;e.Macronsmall=63407;e.Macute=7742;e.Mcircle=9410;e.Mdotaccent=7744;e.Mdotbelow=7746;e.Menarmenian=1348;e.Mmonospace=65325;e.Msmall=63341;e.Mturned=412;e.Mu=924;e.N=78;e.NJ=458;e.Nacute=323;e.Ncaron=327;e.Ncedilla=325;e.Ncircle=9411;e.Ncircumflexbelow=7754;e.Ncommaaccent=325;e.Ndotaccent=7748;e.Ndotbelow=7750;e.Nhookleft=413;e.Nineroman=8552;e.Nj=459;e.Njecyrillic=1034;e.Nlinebelow=7752;e.Nmonospace=65326;e.Nowarmenian=1350;e.Nsmall=63342;e.Ntilde=209;e.Ntildesmall=63473;e.Nu=925;e.O=79;e.OE=338;e.OEsmall=63226;e.Oacute=211;e.Oacutesmall=63475;e.Obarredcyrillic=1256;e.Obarreddieresiscyrillic=1258;e.Obreve=334;e.Ocaron=465;e.Ocenteredtilde=415;e.Ocircle=9412;e.Ocircumflex=212;e.Ocircumflexacute=7888;e.Ocircumflexdotbelow=7896;e.Ocircumflexgrave=7890;e.Ocircumflexhookabove=7892;e.Ocircumflexsmall=63476;e.Ocircumflextilde=7894;e.Ocyrillic=1054;e.Odblacute=336;e.Odblgrave=524;e.Odieresis=214;e.Odieresiscyrillic=1254;e.Odieresissmall=63478;e.Odotbelow=7884;e.Ogoneksmall=63227;e.Ograve=210;e.Ogravesmall=63474;e.Oharmenian=1365;e.Ohm=8486;e.Ohookabove=7886;e.Ohorn=416;e.Ohornacute=7898;e.Ohorndotbelow=7906;e.Ohorngrave=7900;e.Ohornhookabove=7902;e.Ohorntilde=7904;e.Ohungarumlaut=336;e.Oi=418;e.Oinvertedbreve=526;e.Omacron=332;e.Omacronacute=7762;e.Omacrongrave=7760;e.Omega=8486;e.Omegacyrillic=1120;e.Omegagreek=937;e.Omegaroundcyrillic=1146;e.Omegatitlocyrillic=1148;e.Omegatonos=911;e.Omicron=927;e.Omicrontonos=908;e.Omonospace=65327;e.Oneroman=8544;e.Oogonek=490;e.Oogonekmacron=492;e.Oopen=390;e.Oslash=216;e.Oslashacute=510;e.Oslashsmall=63480;e.Osmall=63343;e.Ostrokeacute=510;e.Otcyrillic=1150;e.Otilde=213;e.Otildeacute=7756;e.Otildedieresis=7758;e.Otildesmall=63477;e.P=80;e.Pacute=7764;e.Pcircle=9413;e.Pdotaccent=7766;e.Pecyrillic=1055;e.Peharmenian=1354;e.Pemiddlehookcyrillic=1190;e.Phi=934;e.Phook=420;e.Pi=928;e.Piwrarmenian=1363;e.Pmonospace=65328;e.Psi=936;e.Psicyrillic=1136;e.Psmall=63344;e.Q=81;e.Qcircle=9414;e.Qmonospace=65329;e.Qsmall=63345;e.R=82;e.Raarmenian=1356;e.Racute=340;e.Rcaron=344;e.Rcedilla=342;e.Rcircle=9415;e.Rcommaaccent=342;e.Rdblgrave=528;e.Rdotaccent=7768;e.Rdotbelow=7770;e.Rdotbelowmacron=7772;e.Reharmenian=1360;e.Rfraktur=8476;e.Rho=929;e.Ringsmall=63228;e.Rinvertedbreve=530;e.Rlinebelow=7774;e.Rmonospace=65330;e.Rsmall=63346;e.Rsmallinverted=641;e.Rsmallinvertedsuperior=694;e.S=83;e.SF010000=9484;e.SF020000=9492;e.SF030000=9488;e.SF040000=9496;e.SF050000=9532;e.SF060000=9516;e.SF070000=9524;e.SF080000=9500;e.SF090000=9508;e.SF100000=9472;e.SF110000=9474;e.SF190000=9569;e.SF200000=9570;e.SF210000=9558;e.SF220000=9557;e.SF230000=9571;e.SF240000=9553;e.SF250000=9559;e.SF260000=9565;e.SF270000=9564;e.SF280000=9563;e.SF360000=9566;e.SF370000=9567;e.SF380000=9562;e.SF390000=9556;e.SF400000=9577;e.SF410000=9574;e.SF420000=9568;e.SF430000=9552;e.SF440000=9580;e.SF450000=9575;e.SF460000=9576;e.SF470000=9572;e.SF480000=9573;e.SF490000=9561;e.SF500000=9560;e.SF510000=9554;e.SF520000=9555;e.SF530000=9579;e.SF540000=9578;e.Sacute=346;e.Sacutedotaccent=7780;e.Sampigreek=992;e.Scaron=352;e.Scarondotaccent=7782;e.Scaronsmall=63229;e.Scedilla=350;e.Schwa=399;e.Schwacyrillic=1240;e.Schwadieresiscyrillic=1242;e.Scircle=9416;e.Scircumflex=348;e.Scommaaccent=536;e.Sdotaccent=7776;e.Sdotbelow=7778;e.Sdotbelowdotaccent=7784;e.Seharmenian=1357;e.Sevenroman=8550;e.Shaarmenian=1351;e.Shacyrillic=1064;e.Shchacyrillic=1065;e.Sheicoptic=994;e.Shhacyrillic=1210;e.Shimacoptic=1004;e.Sigma=931;e.Sixroman=8549;e.Smonospace=65331;e.Softsigncyrillic=1068;e.Ssmall=63347;e.Stigmagreek=986;e.T=84;e.Tau=932;e.Tbar=358;e.Tcaron=356;e.Tcedilla=354;e.Tcircle=9417;e.Tcircumflexbelow=7792;e.Tcommaaccent=354;e.Tdotaccent=7786;e.Tdotbelow=7788;e.Tecyrillic=1058;e.Tedescendercyrillic=1196;e.Tenroman=8553;e.Tetsecyrillic=1204;e.Theta=920;e.Thook=428;e.Thorn=222;e.Thornsmall=63486;e.Threeroman=8546;e.Tildesmall=63230;e.Tiwnarmenian=1359;e.Tlinebelow=7790;e.Tmonospace=65332;e.Toarmenian=1337;e.Tonefive=444;e.Tonesix=388;e.Tonetwo=423;e.Tretroflexhook=430;e.Tsecyrillic=1062;e.Tshecyrillic=1035;e.Tsmall=63348;e.Twelveroman=8555;e.Tworoman=8545;e.U=85;e.Uacute=218;e.Uacutesmall=63482;e.Ubreve=364;e.Ucaron=467;e.Ucircle=9418;e.Ucircumflex=219;e.Ucircumflexbelow=7798;e.Ucircumflexsmall=63483;e.Ucyrillic=1059;e.Udblacute=368;e.Udblgrave=532;e.Udieresis=220;e.Udieresisacute=471;e.Udieresisbelow=7794;e.Udieresiscaron=473;e.Udieresiscyrillic=1264;e.Udieresisgrave=475;e.Udieresismacron=469;e.Udieresissmall=63484;e.Udotbelow=7908;e.Ugrave=217;e.Ugravesmall=63481;e.Uhookabove=7910;e.Uhorn=431;e.Uhornacute=7912;e.Uhorndotbelow=7920;e.Uhorngrave=7914;e.Uhornhookabove=7916;e.Uhorntilde=7918;e.Uhungarumlaut=368;e.Uhungarumlautcyrillic=1266;e.Uinvertedbreve=534;e.Ukcyrillic=1144;e.Umacron=362;e.Umacroncyrillic=1262;e.Umacrondieresis=7802;e.Umonospace=65333;e.Uogonek=370;e.Upsilon=933;e.Upsilon1=978;e.Upsilonacutehooksymbolgreek=979;e.Upsilonafrican=433;e.Upsilondieresis=939;e.Upsilondieresishooksymbolgreek=980;e.Upsilonhooksymbol=978;e.Upsilontonos=910;e.Uring=366;e.Ushortcyrillic=1038;e.Usmall=63349;e.Ustraightcyrillic=1198;e.Ustraightstrokecyrillic=1200;e.Utilde=360;e.Utildeacute=7800;e.Utildebelow=7796;e.V=86;e.Vcircle=9419;e.Vdotbelow=7806;e.Vecyrillic=1042;e.Vewarmenian=1358;e.Vhook=434;e.Vmonospace=65334;e.Voarmenian=1352;e.Vsmall=63350;e.Vtilde=7804;e.W=87;e.Wacute=7810;e.Wcircle=9420;e.Wcircumflex=372;e.Wdieresis=7812;e.Wdotaccent=7814;e.Wdotbelow=7816;e.Wgrave=7808;e.Wmonospace=65335;e.Wsmall=63351;e.X=88;e.Xcircle=9421;e.Xdieresis=7820;e.Xdotaccent=7818;e.Xeharmenian=1341;e.Xi=926;e.Xmonospace=65336;e.Xsmall=63352;e.Y=89;e.Yacute=221;e.Yacutesmall=63485;e.Yatcyrillic=1122;e.Ycircle=9422;e.Ycircumflex=374;e.Ydieresis=376;e.Ydieresissmall=63487;e.Ydotaccent=7822;e.Ydotbelow=7924;e.Yericyrillic=1067;e.Yerudieresiscyrillic=1272;e.Ygrave=7922;e.Yhook=435;e.Yhookabove=7926;e.Yiarmenian=1349;e.Yicyrillic=1031;e.Yiwnarmenian=1362;e.Ymonospace=65337;e.Ysmall=63353;e.Ytilde=7928;e.Yusbigcyrillic=1130;e.Yusbigiotifiedcyrillic=1132;e.Yuslittlecyrillic=1126;e.Yuslittleiotifiedcyrillic=1128;e.Z=90;e.Zaarmenian=1334;e.Zacute=377;e.Zcaron=381;e.Zcaronsmall=63231;e.Zcircle=9423;e.Zcircumflex=7824;e.Zdot=379;e.Zdotaccent=379;e.Zdotbelow=7826;e.Zecyrillic=1047;e.Zedescendercyrillic=1176;e.Zedieresiscyrillic=1246;e.Zeta=918;e.Zhearmenian=1338;e.Zhebrevecyrillic=1217;e.Zhecyrillic=1046;e.Zhedescendercyrillic=1174;e.Zhedieresiscyrillic=1244;e.Zlinebelow=7828;e.Zmonospace=65338;e.Zsmall=63354;e.Zstroke=437;e.a=97;e.aabengali=2438;e.aacute=225;e.aadeva=2310;e.aagujarati=2694;e.aagurmukhi=2566;e.aamatragurmukhi=2622;e.aarusquare=13059;e.aavowelsignbengali=2494;e.aavowelsigndeva=2366;e.aavowelsigngujarati=2750;e.abbreviationmarkarmenian=1375;e.abbreviationsigndeva=2416;e.abengali=2437;e.abopomofo=12570;e.abreve=259;e.abreveacute=7855;e.abrevecyrillic=1233;e.abrevedotbelow=7863;e.abrevegrave=7857;e.abrevehookabove=7859;e.abrevetilde=7861;e.acaron=462;e.acircle=9424;e.acircumflex=226;e.acircumflexacute=7845;e.acircumflexdotbelow=7853;e.acircumflexgrave=7847;e.acircumflexhookabove=7849;e.acircumflextilde=7851;e.acute=180;e.acutebelowcmb=791;e.acutecmb=769;e.acutecomb=769;e.acutedeva=2388;e.acutelowmod=719;e.acutetonecmb=833;e.acyrillic=1072;e.adblgrave=513;e.addakgurmukhi=2673;e.adeva=2309;e.adieresis=228;e.adieresiscyrillic=1235;e.adieresismacron=479;e.adotbelow=7841;e.adotmacron=481;e.ae=230;e.aeacute=509;e.aekorean=12624;e.aemacron=483;e.afii00208=8213;e.afii08941=8356;e.afii10017=1040;e.afii10018=1041;e.afii10019=1042;e.afii10020=1043;e.afii10021=1044;e.afii10022=1045;e.afii10023=1025;e.afii10024=1046;e.afii10025=1047;e.afii10026=1048;e.afii10027=1049;e.afii10028=1050;e.afii10029=1051;e.afii10030=1052;e.afii10031=1053;e.afii10032=1054;e.afii10033=1055;e.afii10034=1056;e.afii10035=1057;e.afii10036=1058;e.afii10037=1059;e.afii10038=1060;e.afii10039=1061;e.afii10040=1062;e.afii10041=1063;e.afii10042=1064;e.afii10043=1065;e.afii10044=1066;e.afii10045=1067;e.afii10046=1068;e.afii10047=1069;e.afii10048=1070;e.afii10049=1071;e.afii10050=1168;e.afii10051=1026;e.afii10052=1027;e.afii10053=1028;e.afii10054=1029;e.afii10055=1030;e.afii10056=1031;e.afii10057=1032;e.afii10058=1033;e.afii10059=1034;e.afii10060=1035;e.afii10061=1036;e.afii10062=1038;e.afii10063=63172;e.afii10064=63173;e.afii10065=1072;e.afii10066=1073;e.afii10067=1074;e.afii10068=1075;e.afii10069=1076;e.afii10070=1077;e.afii10071=1105;e.afii10072=1078;e.afii10073=1079;e.afii10074=1080;e.afii10075=1081;e.afii10076=1082;e.afii10077=1083;e.afii10078=1084;e.afii10079=1085;e.afii10080=1086;e.afii10081=1087;e.afii10082=1088;e.afii10083=1089;e.afii10084=1090;e.afii10085=1091;e.afii10086=1092;e.afii10087=1093;e.afii10088=1094;e.afii10089=1095;e.afii10090=1096;e.afii10091=1097;e.afii10092=1098;e.afii10093=1099;e.afii10094=1100;e.afii10095=1101;e.afii10096=1102;e.afii10097=1103;e.afii10098=1169;e.afii10099=1106;e.afii10100=1107;e.afii10101=1108;e.afii10102=1109;e.afii10103=1110;e.afii10104=1111;e.afii10105=1112;e.afii10106=1113;e.afii10107=1114;e.afii10108=1115;e.afii10109=1116;e.afii10110=1118;e.afii10145=1039;e.afii10146=1122;e.afii10147=1138;e.afii10148=1140;e.afii10192=63174;e.afii10193=1119;e.afii10194=1123;e.afii10195=1139;e.afii10196=1141;e.afii10831=63175;e.afii10832=63176;e.afii10846=1241;e.afii299=8206;e.afii300=8207;e.afii301=8205;e.afii57381=1642;e.afii57388=1548;e.afii57392=1632;e.afii57393=1633;e.afii57394=1634;e.afii57395=1635;e.afii57396=1636;e.afii57397=1637;e.afii57398=1638;e.afii57399=1639;e.afii57400=1640;e.afii57401=1641;e.afii57403=1563;e.afii57407=1567;e.afii57409=1569;e.afii57410=1570;e.afii57411=1571;e.afii57412=1572;e.afii57413=1573;e.afii57414=1574;e.afii57415=1575;e.afii57416=1576;e.afii57417=1577;e.afii57418=1578;e.afii57419=1579;e.afii57420=1580;e.afii57421=1581;e.afii57422=1582;e.afii57423=1583;e.afii57424=1584;e.afii57425=1585;e.afii57426=1586;e.afii57427=1587;e.afii57428=1588;e.afii57429=1589;e.afii57430=1590;e.afii57431=1591;e.afii57432=1592;e.afii57433=1593;e.afii57434=1594;e.afii57440=1600;e.afii57441=1601;e.afii57442=1602;e.afii57443=1603;e.afii57444=1604;e.afii57445=1605;e.afii57446=1606;e.afii57448=1608;e.afii57449=1609;e.afii57450=1610;e.afii57451=1611;e.afii57452=1612;e.afii57453=1613;e.afii57454=1614;e.afii57455=1615;e.afii57456=1616;e.afii57457=1617;e.afii57458=1618;e.afii57470=1607;e.afii57505=1700;e.afii57506=1662;e.afii57507=1670;e.afii57508=1688;e.afii57509=1711;e.afii57511=1657;e.afii57512=1672;e.afii57513=1681;e.afii57514=1722;e.afii57519=1746;e.afii57534=1749;e.afii57636=8362;e.afii57645=1470;e.afii57658=1475;e.afii57664=1488;e.afii57665=1489;e.afii57666=1490;e.afii57667=1491;e.afii57668=1492;e.afii57669=1493;e.afii57670=1494;e.afii57671=1495;e.afii57672=1496;e.afii57673=1497;e.afii57674=1498;e.afii57675=1499;e.afii57676=1500;e.afii57677=1501;e.afii57678=1502;e.afii57679=1503;e.afii57680=1504;e.afii57681=1505;e.afii57682=1506;e.afii57683=1507;e.afii57684=1508;e.afii57685=1509;e.afii57686=1510;e.afii57687=1511;e.afii57688=1512;e.afii57689=1513;e.afii57690=1514;e.afii57694=64298;e.afii57695=64299;e.afii57700=64331;e.afii57705=64287;e.afii57716=1520;e.afii57717=1521;e.afii57718=1522;e.afii57723=64309;e.afii57793=1460;e.afii57794=1461;e.afii57795=1462;e.afii57796=1467;e.afii57797=1464;e.afii57798=1463;e.afii57799=1456;e.afii57800=1458;e.afii57801=1457;e.afii57802=1459;e.afii57803=1474;e.afii57804=1473;e.afii57806=1465;e.afii57807=1468;e.afii57839=1469;e.afii57841=1471;e.afii57842=1472;e.afii57929=700;e.afii61248=8453;e.afii61289=8467;e.afii61352=8470;e.afii61573=8236;e.afii61574=8237;e.afii61575=8238;e.afii61664=8204;e.afii63167=1645;e.afii64937=701;e.agrave=224;e.agujarati=2693;e.agurmukhi=2565;e.ahiragana=12354;e.ahookabove=7843;e.aibengali=2448;e.aibopomofo=12574;e.aideva=2320;e.aiecyrillic=1237;e.aigujarati=2704;e.aigurmukhi=2576;e.aimatragurmukhi=2632;e.ainarabic=1593;e.ainfinalarabic=65226;e.aininitialarabic=65227;e.ainmedialarabic=65228;e.ainvertedbreve=515;e.aivowelsignbengali=2504;e.aivowelsigndeva=2376;e.aivowelsigngujarati=2760;e.akatakana=12450;e.akatakanahalfwidth=65393;e.akorean=12623;e.alef=1488;e.alefarabic=1575;e.alefdageshhebrew=64304;e.aleffinalarabic=65166;e.alefhamzaabovearabic=1571;e.alefhamzaabovefinalarabic=65156;e.alefhamzabelowarabic=1573;e.alefhamzabelowfinalarabic=65160;e.alefhebrew=1488;e.aleflamedhebrew=64335;e.alefmaddaabovearabic=1570;e.alefmaddaabovefinalarabic=65154;e.alefmaksuraarabic=1609;e.alefmaksurafinalarabic=65264;e.alefmaksurainitialarabic=65267;e.alefmaksuramedialarabic=65268;e.alefpatahhebrew=64302;e.alefqamatshebrew=64303;e.aleph=8501;e.allequal=8780;e.alpha=945;e.alphatonos=940;e.amacron=257;e.amonospace=65345;e.ampersand=38;e.ampersandmonospace=65286;e.ampersandsmall=63270;e.amsquare=13250;e.anbopomofo=12578;e.angbopomofo=12580;e.angbracketleft=12296;e.angbracketright=12297;e.angkhankhuthai=3674;e.angle=8736;e.anglebracketleft=12296;e.anglebracketleftvertical=65087;e.anglebracketright=12297;e.anglebracketrightvertical=65088;e.angleleft=9001;e.angleright=9002;e.angstrom=8491;e.anoteleia=903;e.anudattadeva=2386;e.anusvarabengali=2434;e.anusvaradeva=2306;e.anusvaragujarati=2690;e.aogonek=261;e.apaatosquare=13056;e.aparen=9372;e.apostrophearmenian=1370;e.apostrophemod=700;e.apple=63743;e.approaches=8784;e.approxequal=8776;e.approxequalorimage=8786;e.approximatelyequal=8773;e.araeaekorean=12686;e.araeakorean=12685;e.arc=8978;e.arighthalfring=7834;e.aring=229;e.aringacute=507;e.aringbelow=7681;e.arrowboth=8596;e.arrowdashdown=8675;e.arrowdashleft=8672;e.arrowdashright=8674;e.arrowdashup=8673;e.arrowdblboth=8660;e.arrowdbldown=8659;e.arrowdblleft=8656;e.arrowdblright=8658;e.arrowdblup=8657;e.arrowdown=8595;e.arrowdownleft=8601;e.arrowdownright=8600;e.arrowdownwhite=8681;e.arrowheaddownmod=709;e.arrowheadleftmod=706;e.arrowheadrightmod=707;e.arrowheadupmod=708;e.arrowhorizex=63719;e.arrowleft=8592;e.arrowleftdbl=8656;e.arrowleftdblstroke=8653;e.arrowleftoverright=8646;e.arrowleftwhite=8678;e.arrowright=8594;e.arrowrightdblstroke=8655;e.arrowrightheavy=10142;e.arrowrightoverleft=8644;e.arrowrightwhite=8680;e.arrowtableft=8676;e.arrowtabright=8677;e.arrowup=8593;e.arrowupdn=8597;e.arrowupdnbse=8616;e.arrowupdownbase=8616;e.arrowupleft=8598;e.arrowupleftofdown=8645;e.arrowupright=8599;e.arrowupwhite=8679;e.arrowvertex=63718;e.asciicircum=94;e.asciicircummonospace=65342;e.asciitilde=126;e.asciitildemonospace=65374;e.ascript=593;e.ascriptturned=594;e.asmallhiragana=12353;e.asmallkatakana=12449;e.asmallkatakanahalfwidth=65383;e.asterisk=42;e.asteriskaltonearabic=1645;e.asteriskarabic=1645;e.asteriskmath=8727;e.asteriskmonospace=65290;e.asterisksmall=65121;e.asterism=8258;e.asuperior=63209;e.asymptoticallyequal=8771;e.at=64;e.atilde=227;e.atmonospace=65312;e.atsmall=65131;e.aturned=592;e.aubengali=2452;e.aubopomofo=12576;e.audeva=2324;e.augujarati=2708;e.augurmukhi=2580;e.aulengthmarkbengali=2519;e.aumatragurmukhi=2636;e.auvowelsignbengali=2508;e.auvowelsigndeva=2380;e.auvowelsigngujarati=2764;e.avagrahadeva=2365;e.aybarmenian=1377;e.ayin=1506;e.ayinaltonehebrew=64288;e.ayinhebrew=1506;e.b=98;e.babengali=2476;e.backslash=92;e.backslashmonospace=65340;e.badeva=2348;e.bagujarati=2732;e.bagurmukhi=2604;e.bahiragana=12400;e.bahtthai=3647;e.bakatakana=12496;e.bar=124;e.barmonospace=65372;e.bbopomofo=12549;e.bcircle=9425;e.bdotaccent=7683;e.bdotbelow=7685;e.beamedsixteenthnotes=9836;e.because=8757;e.becyrillic=1073;e.beharabic=1576;e.behfinalarabic=65168;e.behinitialarabic=65169;e.behiragana=12409;e.behmedialarabic=65170;e.behmeeminitialarabic=64671;e.behmeemisolatedarabic=64520;e.behnoonfinalarabic=64621;e.bekatakana=12505;e.benarmenian=1378;e.bet=1489;e.beta=946;e.betasymbolgreek=976;e.betdagesh=64305;e.betdageshhebrew=64305;e.bethebrew=1489;e.betrafehebrew=64332;e.bhabengali=2477;e.bhadeva=2349;e.bhagujarati=2733;e.bhagurmukhi=2605;e.bhook=595;e.bihiragana=12403;e.bikatakana=12499;e.bilabialclick=664;e.bindigurmukhi=2562;e.birusquare=13105;e.blackcircle=9679;e.blackdiamond=9670;e.blackdownpointingtriangle=9660;e.blackleftpointingpointer=9668;e.blackleftpointingtriangle=9664;e.blacklenticularbracketleft=12304;e.blacklenticularbracketleftvertical=65083;e.blacklenticularbracketright=12305;e.blacklenticularbracketrightvertical=65084;e.blacklowerlefttriangle=9699;e.blacklowerrighttriangle=9698;e.blackrectangle=9644;e.blackrightpointingpointer=9658;e.blackrightpointingtriangle=9654;e.blacksmallsquare=9642;e.blacksmilingface=9787;e.blacksquare=9632;e.blackstar=9733;e.blackupperlefttriangle=9700;e.blackupperrighttriangle=9701;e.blackuppointingsmalltriangle=9652;e.blackuppointingtriangle=9650;e.blank=9251;e.blinebelow=7687;e.block=9608;e.bmonospace=65346;e.bobaimaithai=3610;e.bohiragana=12412;e.bokatakana=12508;e.bparen=9373;e.bqsquare=13251;e.braceex=63732;e.braceleft=123;e.braceleftbt=63731;e.braceleftmid=63730;e.braceleftmonospace=65371;e.braceleftsmall=65115;e.bracelefttp=63729;e.braceleftvertical=65079;e.braceright=125;e.bracerightbt=63742;e.bracerightmid=63741;e.bracerightmonospace=65373;e.bracerightsmall=65116;e.bracerighttp=63740;e.bracerightvertical=65080;e.bracketleft=91;e.bracketleftbt=63728;e.bracketleftex=63727;e.bracketleftmonospace=65339;e.bracketlefttp=63726;e.bracketright=93;e.bracketrightbt=63739;e.bracketrightex=63738;e.bracketrightmonospace=65341;e.bracketrighttp=63737;e.breve=728;e.brevebelowcmb=814;e.brevecmb=774;e.breveinvertedbelowcmb=815;e.breveinvertedcmb=785;e.breveinverteddoublecmb=865;e.bridgebelowcmb=810;e.bridgeinvertedbelowcmb=826;e.brokenbar=166;e.bstroke=384;e.bsuperior=63210;e.btopbar=387;e.buhiragana=12406;e.bukatakana=12502;e.bullet=8226;e.bulletinverse=9688;e.bulletoperator=8729;e.bullseye=9678;e.c=99;e.caarmenian=1390;e.cabengali=2458;e.cacute=263;e.cadeva=2330;e.cagujarati=2714;e.cagurmukhi=2586;e.calsquare=13192;e.candrabindubengali=2433;e.candrabinducmb=784;e.candrabindudeva=2305;e.candrabindugujarati=2689;e.capslock=8682;e.careof=8453;e.caron=711;e.caronbelowcmb=812;e.caroncmb=780;e.carriagereturn=8629;e.cbopomofo=12568;e.ccaron=269;e.ccedilla=231;e.ccedillaacute=7689;e.ccircle=9426;e.ccircumflex=265;e.ccurl=597;e.cdot=267;e.cdotaccent=267;e.cdsquare=13253;e.cedilla=184;e.cedillacmb=807;e.cent=162;e.centigrade=8451;e.centinferior=63199;e.centmonospace=65504;e.centoldstyle=63394;e.centsuperior=63200;e.chaarmenian=1401;e.chabengali=2459;e.chadeva=2331;e.chagujarati=2715;e.chagurmukhi=2587;e.chbopomofo=12564;e.cheabkhasiancyrillic=1213;e.checkmark=10003;e.checyrillic=1095;e.chedescenderabkhasiancyrillic=1215;e.chedescendercyrillic=1207;e.chedieresiscyrillic=1269;e.cheharmenian=1395;e.chekhakassiancyrillic=1228;e.cheverticalstrokecyrillic=1209;e.chi=967;e.chieuchacirclekorean=12919;e.chieuchaparenkorean=12823;e.chieuchcirclekorean=12905;e.chieuchkorean=12618;e.chieuchparenkorean=12809;e.chochangthai=3594;e.chochanthai=3592;e.chochingthai=3593;e.chochoethai=3596;e.chook=392;e.cieucacirclekorean=12918;e.cieucaparenkorean=12822;e.cieuccirclekorean=12904;e.cieuckorean=12616;e.cieucparenkorean=12808;e.cieucuparenkorean=12828;e.circle=9675;e.circlecopyrt=169;e.circlemultiply=8855;e.circleot=8857;e.circleplus=8853;e.circlepostalmark=12342;e.circlewithlefthalfblack=9680;e.circlewithrighthalfblack=9681;e.circumflex=710;e.circumflexbelowcmb=813;e.circumflexcmb=770;e.clear=8999;e.clickalveolar=450;e.clickdental=448;e.clicklateral=449;e.clickretroflex=451;e.club=9827;e.clubsuitblack=9827;e.clubsuitwhite=9831;e.cmcubedsquare=13220;e.cmonospace=65347;e.cmsquaredsquare=13216;e.coarmenian=1409;e.colon=58;e.colonmonetary=8353;e.colonmonospace=65306;e.colonsign=8353;e.colonsmall=65109;e.colontriangularhalfmod=721;e.colontriangularmod=720;e.comma=44;e.commaabovecmb=787;e.commaaboverightcmb=789;e.commaaccent=63171;e.commaarabic=1548;e.commaarmenian=1373;e.commainferior=63201;e.commamonospace=65292;e.commareversedabovecmb=788;e.commareversedmod=701;e.commasmall=65104;e.commasuperior=63202;e.commaturnedabovecmb=786;e.commaturnedmod=699;e.compass=9788;e.congruent=8773;e.contourintegral=8750;e.control=8963;e.controlACK=6;e.controlBEL=7;e.controlBS=8;e.controlCAN=24;e.controlCR=13;e.controlDC1=17;e.controlDC2=18;e.controlDC3=19;e.controlDC4=20;e.controlDEL=127;e.controlDLE=16;e.controlEM=25;e.controlENQ=5;e.controlEOT=4;e.controlESC=27;e.controlETB=23;e.controlETX=3;e.controlFF=12;e.controlFS=28;e.controlGS=29;e.controlHT=9;e.controlLF=10;e.controlNAK=21;e.controlNULL=0;e.controlRS=30;e.controlSI=15;e.controlSO=14;e.controlSOT=2;e.controlSTX=1;e.controlSUB=26;e.controlSYN=22;e.controlUS=31;e.controlVT=11;e.copyright=169;e.copyrightsans=63721;e.copyrightserif=63193;e.cornerbracketleft=12300;e.cornerbracketlefthalfwidth=65378;e.cornerbracketleftvertical=65089;e.cornerbracketright=12301;e.cornerbracketrighthalfwidth=65379;e.cornerbracketrightvertical=65090;e.corporationsquare=13183;e.cosquare=13255;e.coverkgsquare=13254;e.cparen=9374;e.cruzeiro=8354;e.cstretched=663;e.curlyand=8911;e.curlyor=8910;e.currency=164;e.cyrBreve=63185;e.cyrFlex=63186;e.cyrbreve=63188;e.cyrflex=63189;e.d=100;e.daarmenian=1380;e.dabengali=2470;e.dadarabic=1590;e.dadeva=2342;e.dadfinalarabic=65214;e.dadinitialarabic=65215;e.dadmedialarabic=65216;e.dagesh=1468;e.dageshhebrew=1468;e.dagger=8224;e.daggerdbl=8225;e.dagujarati=2726;e.dagurmukhi=2598;e.dahiragana=12384;e.dakatakana=12480;e.dalarabic=1583;e.dalet=1491;e.daletdagesh=64307;e.daletdageshhebrew=64307;e.dalethebrew=1491;e.dalfinalarabic=65194;e.dammaarabic=1615;e.dammalowarabic=1615;e.dammatanaltonearabic=1612;e.dammatanarabic=1612;e.danda=2404;e.dargahebrew=1447;e.dargalefthebrew=1447;e.dasiapneumatacyrilliccmb=1157;e.dblGrave=63187;e.dblanglebracketleft=12298;e.dblanglebracketleftvertical=65085;e.dblanglebracketright=12299;e.dblanglebracketrightvertical=65086;e.dblarchinvertedbelowcmb=811;e.dblarrowleft=8660;e.dblarrowright=8658;e.dbldanda=2405;e.dblgrave=63190;e.dblgravecmb=783;e.dblintegral=8748;e.dbllowline=8215;e.dbllowlinecmb=819;e.dbloverlinecmb=831;e.dblprimemod=698;e.dblverticalbar=8214;e.dblverticallineabovecmb=782;e.dbopomofo=12553;e.dbsquare=13256;e.dcaron=271;e.dcedilla=7697;e.dcircle=9427;e.dcircumflexbelow=7699;e.dcroat=273;e.ddabengali=2465;e.ddadeva=2337;e.ddagujarati=2721;e.ddagurmukhi=2593;e.ddalarabic=1672;e.ddalfinalarabic=64393;e.dddhadeva=2396;e.ddhabengali=2466;e.ddhadeva=2338;e.ddhagujarati=2722;e.ddhagurmukhi=2594;e.ddotaccent=7691;e.ddotbelow=7693;e.decimalseparatorarabic=1643;e.decimalseparatorpersian=1643;e.decyrillic=1076;e.degree=176;e.dehihebrew=1453;e.dehiragana=12391;e.deicoptic=1007;e.dekatakana=12487;e.deleteleft=9003;e.deleteright=8998;e.delta=948;e.deltaturned=397;e.denominatorminusonenumeratorbengali=2552;e.dezh=676;e.dhabengali=2471;e.dhadeva=2343;e.dhagujarati=2727;e.dhagurmukhi=2599;e.dhook=599;e.dialytikatonos=901;e.dialytikatonoscmb=836;e.diamond=9830;e.diamondsuitwhite=9826;e.dieresis=168;e.dieresisacute=63191;e.dieresisbelowcmb=804;e.dieresiscmb=776;e.dieresisgrave=63192;e.dieresistonos=901;e.dihiragana=12386;e.dikatakana=12482;e.dittomark=12291;e.divide=247;e.divides=8739;e.divisionslash=8725;e.djecyrillic=1106;e.dkshade=9619;e.dlinebelow=7695;e.dlsquare=13207;e.dmacron=273;e.dmonospace=65348;e.dnblock=9604;e.dochadathai=3598;e.dodekthai=3604;e.dohiragana=12393;e.dokatakana=12489;e.dollar=36;e.dollarinferior=63203;e.dollarmonospace=65284;e.dollaroldstyle=63268;e.dollarsmall=65129;e.dollarsuperior=63204;e.dong=8363;e.dorusquare=13094;e.dotaccent=729;e.dotaccentcmb=775;e.dotbelowcmb=803;e.dotbelowcomb=803;e.dotkatakana=12539;e.dotlessi=305;e.dotlessj=63166;e.dotlessjstrokehook=644;e.dotmath=8901;e.dottedcircle=9676;e.doubleyodpatah=64287;e.doubleyodpatahhebrew=64287;e.downtackbelowcmb=798;e.downtackmod=725;e.dparen=9375;e.dsuperior=63211;e.dtail=598;e.dtopbar=396;e.duhiragana=12389;e.dukatakana=12485;e.dz=499;e.dzaltone=675;e.dzcaron=454;e.dzcurl=677;e.dzeabkhasiancyrillic=1249;e.dzecyrillic=1109;e.dzhecyrillic=1119;e.e=101;e.eacute=233;e.earth=9793;e.ebengali=2447;e.ebopomofo=12572;e.ebreve=277;e.ecandradeva=2317;e.ecandragujarati=2701;e.ecandravowelsigndeva=2373;e.ecandravowelsigngujarati=2757;e.ecaron=283;e.ecedillabreve=7709;e.echarmenian=1381;e.echyiwnarmenian=1415;e.ecircle=9428;e.ecircumflex=234;e.ecircumflexacute=7871;e.ecircumflexbelow=7705;e.ecircumflexdotbelow=7879;e.ecircumflexgrave=7873;e.ecircumflexhookabove=7875;e.ecircumflextilde=7877;e.ecyrillic=1108;e.edblgrave=517;e.edeva=2319;e.edieresis=235;e.edot=279;e.edotaccent=279;e.edotbelow=7865;e.eegurmukhi=2575;e.eematragurmukhi=2631;e.efcyrillic=1092;e.egrave=232;e.egujarati=2703;e.eharmenian=1383;e.ehbopomofo=12573;e.ehiragana=12360;e.ehookabove=7867;e.eibopomofo=12575;e.eight=56;e.eightarabic=1640;e.eightbengali=2542;e.eightcircle=9319;e.eightcircleinversesansserif=10129;e.eightdeva=2414;e.eighteencircle=9329;e.eighteenparen=9349;e.eighteenperiod=9369;e.eightgujarati=2798;e.eightgurmukhi=2670;e.eighthackarabic=1640;e.eighthangzhou=12328;e.eighthnotebeamed=9835;e.eightideographicparen=12839;e.eightinferior=8328;e.eightmonospace=65304;e.eightoldstyle=63288;e.eightparen=9339;e.eightperiod=9359;e.eightpersian=1784;e.eightroman=8567;e.eightsuperior=8312;e.eightthai=3672;e.einvertedbreve=519;e.eiotifiedcyrillic=1125;e.ekatakana=12456;e.ekatakanahalfwidth=65396;e.ekonkargurmukhi=2676;e.ekorean=12628;e.elcyrillic=1083;e.element=8712;e.elevencircle=9322;e.elevenparen=9342;e.elevenperiod=9362;e.elevenroman=8570;e.ellipsis=8230;e.ellipsisvertical=8942;e.emacron=275;e.emacronacute=7703;e.emacrongrave=7701;e.emcyrillic=1084;e.emdash=8212;e.emdashvertical=65073;e.emonospace=65349;e.emphasismarkarmenian=1371;e.emptyset=8709;e.enbopomofo=12579;e.encyrillic=1085;e.endash=8211;e.endashvertical=65074;e.endescendercyrillic=1187;e.eng=331;e.engbopomofo=12581;e.enghecyrillic=1189;e.enhookcyrillic=1224;e.enspace=8194;e.eogonek=281;e.eokorean=12627;e.eopen=603;e.eopenclosed=666;e.eopenreversed=604;e.eopenreversedclosed=606;e.eopenreversedhook=605;e.eparen=9376;e.epsilon=949;e.epsilontonos=941;e.equal=61;e.equalmonospace=65309;e.equalsmall=65126;e.equalsuperior=8316;e.equivalence=8801;e.erbopomofo=12582;e.ercyrillic=1088;e.ereversed=600;e.ereversedcyrillic=1101;e.escyrillic=1089;e.esdescendercyrillic=1195;e.esh=643;e.eshcurl=646;e.eshortdeva=2318;e.eshortvowelsigndeva=2374;e.eshreversedloop=426;e.eshsquatreversed=645;e.esmallhiragana=12359;e.esmallkatakana=12455;e.esmallkatakanahalfwidth=65386;e.estimated=8494;e.esuperior=63212;e.eta=951;e.etarmenian=1384;e.etatonos=942;e.eth=240;e.etilde=7869;e.etildebelow=7707;e.etnahtafoukhhebrew=1425;e.etnahtafoukhlefthebrew=1425;e.etnahtahebrew=1425;e.etnahtalefthebrew=1425;e.eturned=477;e.eukorean=12641;e.euro=8364;e.evowelsignbengali=2503;e.evowelsigndeva=2375;e.evowelsigngujarati=2759;e.exclam=33;e.exclamarmenian=1372;e.exclamdbl=8252;e.exclamdown=161;e.exclamdownsmall=63393;e.exclammonospace=65281;e.exclamsmall=63265;e.existential=8707;e.ezh=658;e.ezhcaron=495;e.ezhcurl=659;e.ezhreversed=441;e.ezhtail=442;e.f=102;e.fadeva=2398;e.fagurmukhi=2654;e.fahrenheit=8457;e.fathaarabic=1614;e.fathalowarabic=1614;e.fathatanarabic=1611;e.fbopomofo=12552;e.fcircle=9429;e.fdotaccent=7711;e.feharabic=1601;e.feharmenian=1414;e.fehfinalarabic=65234;e.fehinitialarabic=65235;e.fehmedialarabic=65236;e.feicoptic=997;e.female=9792;e.ff=64256;e.f_f=64256;e.ffi=64259;e.f_f_i=64259;e.ffl=64260;e.f_f_l=64260;e.fi=64257;e.f_i=64257;e.fifteencircle=9326;e.fifteenparen=9346;e.fifteenperiod=9366;e.figuredash=8210;e.filledbox=9632;e.filledrect=9644;e.finalkaf=1498;e.finalkafdagesh=64314;e.finalkafdageshhebrew=64314;e.finalkafhebrew=1498;e.finalmem=1501;e.finalmemhebrew=1501;e.finalnun=1503;e.finalnunhebrew=1503;e.finalpe=1507;e.finalpehebrew=1507;e.finaltsadi=1509;e.finaltsadihebrew=1509;e.firsttonechinese=713;e.fisheye=9673;e.fitacyrillic=1139;e.five=53;e.fivearabic=1637;e.fivebengali=2539;e.fivecircle=9316;e.fivecircleinversesansserif=10126;e.fivedeva=2411;e.fiveeighths=8541;e.fivegujarati=2795;e.fivegurmukhi=2667;e.fivehackarabic=1637;e.fivehangzhou=12325;e.fiveideographicparen=12836;e.fiveinferior=8325;e.fivemonospace=65301;e.fiveoldstyle=63285;e.fiveparen=9336;e.fiveperiod=9356;e.fivepersian=1781;e.fiveroman=8564;e.fivesuperior=8309;e.fivethai=3669;e.fl=64258;e.f_l=64258;e.florin=402;e.fmonospace=65350;e.fmsquare=13209;e.fofanthai=3615;e.fofathai=3613;e.fongmanthai=3663;e.forall=8704;e.four=52;e.fourarabic=1636;e.fourbengali=2538;e.fourcircle=9315;e.fourcircleinversesansserif=10125;e.fourdeva=2410;e.fourgujarati=2794;e.fourgurmukhi=2666;e.fourhackarabic=1636;e.fourhangzhou=12324;e.fourideographicparen=12835;e.fourinferior=8324;e.fourmonospace=65300;e.fournumeratorbengali=2551;e.fouroldstyle=63284;e.fourparen=9335;e.fourperiod=9355;e.fourpersian=1780;e.fourroman=8563;e.foursuperior=8308;e.fourteencircle=9325;e.fourteenparen=9345;e.fourteenperiod=9365;e.fourthai=3668;e.fourthtonechinese=715;e.fparen=9377;e.fraction=8260;e.franc=8355;e.g=103;e.gabengali=2455;e.gacute=501;e.gadeva=2327;e.gafarabic=1711;e.gaffinalarabic=64403;e.gafinitialarabic=64404;e.gafmedialarabic=64405;e.gagujarati=2711;e.gagurmukhi=2583;e.gahiragana=12364;e.gakatakana=12460;e.gamma=947;e.gammalatinsmall=611;e.gammasuperior=736;e.gangiacoptic=1003;e.gbopomofo=12557;e.gbreve=287;e.gcaron=487;e.gcedilla=291;e.gcircle=9430;e.gcircumflex=285;e.gcommaaccent=291;e.gdot=289;e.gdotaccent=289;e.gecyrillic=1075;e.gehiragana=12370;e.gekatakana=12466;e.geometricallyequal=8785;e.gereshaccenthebrew=1436;e.gereshhebrew=1523;e.gereshmuqdamhebrew=1437;e.germandbls=223;e.gershayimaccenthebrew=1438;e.gershayimhebrew=1524;e.getamark=12307;e.ghabengali=2456;e.ghadarmenian=1394;e.ghadeva=2328;e.ghagujarati=2712;e.ghagurmukhi=2584;e.ghainarabic=1594;e.ghainfinalarabic=65230;e.ghaininitialarabic=65231;e.ghainmedialarabic=65232;e.ghemiddlehookcyrillic=1173;e.ghestrokecyrillic=1171;e.gheupturncyrillic=1169;e.ghhadeva=2394;e.ghhagurmukhi=2650;e.ghook=608;e.ghzsquare=13203;e.gihiragana=12366;e.gikatakana=12462;e.gimarmenian=1379;e.gimel=1490;e.gimeldagesh=64306;e.gimeldageshhebrew=64306;e.gimelhebrew=1490;e.gjecyrillic=1107;e.glottalinvertedstroke=446;e.glottalstop=660;e.glottalstopinverted=662;e.glottalstopmod=704;e.glottalstopreversed=661;e.glottalstopreversedmod=705;e.glottalstopreversedsuperior=740;e.glottalstopstroke=673;e.glottalstopstrokereversed=674;e.gmacron=7713;e.gmonospace=65351;e.gohiragana=12372;e.gokatakana=12468;e.gparen=9378;e.gpasquare=13228;e.gradient=8711;e.grave=96;e.gravebelowcmb=790;e.gravecmb=768;e.gravecomb=768;e.gravedeva=2387;e.gravelowmod=718;e.gravemonospace=65344;e.gravetonecmb=832;e.greater=62;e.greaterequal=8805;e.greaterequalorless=8923;e.greatermonospace=65310;e.greaterorequivalent=8819;e.greaterorless=8823;e.greateroverequal=8807;e.greatersmall=65125;e.gscript=609;e.gstroke=485;e.guhiragana=12368;e.guillemotleft=171;e.guillemotright=187;e.guilsinglleft=8249;e.guilsinglright=8250;e.gukatakana=12464;e.guramusquare=13080;e.gysquare=13257;e.h=104;e.haabkhasiancyrillic=1193;e.haaltonearabic=1729;e.habengali=2489;e.hadescendercyrillic=1203;e.hadeva=2361;e.hagujarati=2745;e.hagurmukhi=2617;e.haharabic=1581;e.hahfinalarabic=65186;e.hahinitialarabic=65187;e.hahiragana=12399;e.hahmedialarabic=65188;e.haitusquare=13098;e.hakatakana=12495;e.hakatakanahalfwidth=65418;e.halantgurmukhi=2637;e.hamzaarabic=1569;e.hamzalowarabic=1569;e.hangulfiller=12644;e.hardsigncyrillic=1098;e.harpoonleftbarbup=8636;e.harpoonrightbarbup=8640;e.hasquare=13258;e.hatafpatah=1458;e.hatafpatah16=1458;e.hatafpatah23=1458;e.hatafpatah2f=1458;e.hatafpatahhebrew=1458;e.hatafpatahnarrowhebrew=1458;e.hatafpatahquarterhebrew=1458;e.hatafpatahwidehebrew=1458;e.hatafqamats=1459;e.hatafqamats1b=1459;e.hatafqamats28=1459;e.hatafqamats34=1459;e.hatafqamatshebrew=1459;e.hatafqamatsnarrowhebrew=1459;e.hatafqamatsquarterhebrew=1459;e.hatafqamatswidehebrew=1459;e.hatafsegol=1457;e.hatafsegol17=1457;e.hatafsegol24=1457;e.hatafsegol30=1457;e.hatafsegolhebrew=1457;e.hatafsegolnarrowhebrew=1457;e.hatafsegolquarterhebrew=1457;e.hatafsegolwidehebrew=1457;e.hbar=295;e.hbopomofo=12559;e.hbrevebelow=7723;e.hcedilla=7721;e.hcircle=9431;e.hcircumflex=293;e.hdieresis=7719;e.hdotaccent=7715;e.hdotbelow=7717;e.he=1492;e.heart=9829;e.heartsuitblack=9829;e.heartsuitwhite=9825;e.hedagesh=64308;e.hedageshhebrew=64308;e.hehaltonearabic=1729;e.heharabic=1607;e.hehebrew=1492;e.hehfinalaltonearabic=64423;e.hehfinalalttwoarabic=65258;e.hehfinalarabic=65258;e.hehhamzaabovefinalarabic=64421;e.hehhamzaaboveisolatedarabic=64420;e.hehinitialaltonearabic=64424;e.hehinitialarabic=65259;e.hehiragana=12408;e.hehmedialaltonearabic=64425;e.hehmedialarabic=65260;e.heiseierasquare=13179;e.hekatakana=12504;e.hekatakanahalfwidth=65421;e.hekutaarusquare=13110;e.henghook=615;e.herutusquare=13113;e.het=1495;e.hethebrew=1495;e.hhook=614;e.hhooksuperior=689;e.hieuhacirclekorean=12923;e.hieuhaparenkorean=12827;e.hieuhcirclekorean=12909;e.hieuhkorean=12622;e.hieuhparenkorean=12813;e.hihiragana=12402;e.hikatakana=12498;e.hikatakanahalfwidth=65419;e.hiriq=1460;e.hiriq14=1460;e.hiriq21=1460;e.hiriq2d=1460;e.hiriqhebrew=1460;e.hiriqnarrowhebrew=1460;e.hiriqquarterhebrew=1460;e.hiriqwidehebrew=1460;e.hlinebelow=7830;e.hmonospace=65352;e.hoarmenian=1392;e.hohipthai=3627;e.hohiragana=12411;e.hokatakana=12507;e.hokatakanahalfwidth=65422;e.holam=1465;e.holam19=1465;e.holam26=1465;e.holam32=1465;e.holamhebrew=1465;e.holamnarrowhebrew=1465;e.holamquarterhebrew=1465;e.holamwidehebrew=1465;e.honokhukthai=3630;e.hookabovecomb=777;e.hookcmb=777;e.hookpalatalizedbelowcmb=801;e.hookretroflexbelowcmb=802;e.hoonsquare=13122;e.horicoptic=1001;e.horizontalbar=8213;e.horncmb=795;e.hotsprings=9832;e.house=8962;e.hparen=9379;e.hsuperior=688;e.hturned=613;e.huhiragana=12405;e.huiitosquare=13107;e.hukatakana=12501;e.hukatakanahalfwidth=65420;e.hungarumlaut=733;e.hungarumlautcmb=779;e.hv=405;e.hyphen=45;e.hypheninferior=63205;e.hyphenmonospace=65293;e.hyphensmall=65123;e.hyphensuperior=63206;e.hyphentwo=8208;e.i=105;e.iacute=237;e.iacyrillic=1103;e.ibengali=2439;e.ibopomofo=12583;e.ibreve=301;e.icaron=464;e.icircle=9432;e.icircumflex=238;e.icyrillic=1110;e.idblgrave=521;e.ideographearthcircle=12943;e.ideographfirecircle=12939;e.ideographicallianceparen=12863;e.ideographiccallparen=12858;e.ideographiccentrecircle=12965;e.ideographicclose=12294;e.ideographiccomma=12289;e.ideographiccommaleft=65380;e.ideographiccongratulationparen=12855;e.ideographiccorrectcircle=12963;e.ideographicearthparen=12847;e.ideographicenterpriseparen=12861;e.ideographicexcellentcircle=12957;e.ideographicfestivalparen=12864;e.ideographicfinancialcircle=12950;e.ideographicfinancialparen=12854;e.ideographicfireparen=12843;e.ideographichaveparen=12850;e.ideographichighcircle=12964;e.ideographiciterationmark=12293;e.ideographiclaborcircle=12952;e.ideographiclaborparen=12856;e.ideographicleftcircle=12967;e.ideographiclowcircle=12966;e.ideographicmedicinecircle=12969;e.ideographicmetalparen=12846;e.ideographicmoonparen=12842;e.ideographicnameparen=12852;e.ideographicperiod=12290;e.ideographicprintcircle=12958;e.ideographicreachparen=12867;e.ideographicrepresentparen=12857;e.ideographicresourceparen=12862;e.ideographicrightcircle=12968;e.ideographicsecretcircle=12953;e.ideographicselfparen=12866;e.ideographicsocietyparen=12851;e.ideographicspace=12288;e.ideographicspecialparen=12853;e.ideographicstockparen=12849;e.ideographicstudyparen=12859;e.ideographicsunparen=12848;e.ideographicsuperviseparen=12860;e.ideographicwaterparen=12844;e.ideographicwoodparen=12845;e.ideographiczero=12295;e.ideographmetalcircle=12942;e.ideographmooncircle=12938;e.ideographnamecircle=12948;e.ideographsuncircle=12944;e.ideographwatercircle=12940;e.ideographwoodcircle=12941;e.ideva=2311;e.idieresis=239;e.idieresisacute=7727;e.idieresiscyrillic=1253;e.idotbelow=7883;e.iebrevecyrillic=1239;e.iecyrillic=1077;e.ieungacirclekorean=12917;e.ieungaparenkorean=12821;e.ieungcirclekorean=12903;e.ieungkorean=12615;e.ieungparenkorean=12807;e.igrave=236;e.igujarati=2695;e.igurmukhi=2567;e.ihiragana=12356;e.ihookabove=7881;e.iibengali=2440;e.iicyrillic=1080;e.iideva=2312;e.iigujarati=2696;e.iigurmukhi=2568;e.iimatragurmukhi=2624;e.iinvertedbreve=523;e.iishortcyrillic=1081;e.iivowelsignbengali=2496;e.iivowelsigndeva=2368;e.iivowelsigngujarati=2752;e.ij=307;e.ikatakana=12452;e.ikatakanahalfwidth=65394;e.ikorean=12643;e.ilde=732;e.iluyhebrew=1452;e.imacron=299;e.imacroncyrillic=1251;e.imageorapproximatelyequal=8787;e.imatragurmukhi=2623;e.imonospace=65353;e.increment=8710;e.infinity=8734;e.iniarmenian=1387;e.integral=8747;e.integralbottom=8993;e.integralbt=8993;e.integralex=63733;e.integraltop=8992;e.integraltp=8992;e.intersection=8745;e.intisquare=13061;e.invbullet=9688;e.invcircle=9689;e.invsmileface=9787;e.iocyrillic=1105;e.iogonek=303;e.iota=953;e.iotadieresis=970;e.iotadieresistonos=912;e.iotalatin=617;e.iotatonos=943;e.iparen=9380;e.irigurmukhi=2674;e.ismallhiragana=12355;e.ismallkatakana=12451;e.ismallkatakanahalfwidth=65384;e.issharbengali=2554;e.istroke=616;e.isuperior=63213;e.iterationhiragana=12445;e.iterationkatakana=12541;e.itilde=297;e.itildebelow=7725;e.iubopomofo=12585;e.iucyrillic=1102;e.ivowelsignbengali=2495;e.ivowelsigndeva=2367;e.ivowelsigngujarati=2751;e.izhitsacyrillic=1141;e.izhitsadblgravecyrillic=1143;e.j=106;e.jaarmenian=1393;e.jabengali=2460;e.jadeva=2332;e.jagujarati=2716;e.jagurmukhi=2588;e.jbopomofo=12560;e.jcaron=496;e.jcircle=9433;e.jcircumflex=309;e.jcrossedtail=669;e.jdotlessstroke=607;e.jecyrillic=1112;e.jeemarabic=1580;e.jeemfinalarabic=65182;e.jeeminitialarabic=65183;e.jeemmedialarabic=65184;e.jeharabic=1688;e.jehfinalarabic=64395;e.jhabengali=2461;e.jhadeva=2333;e.jhagujarati=2717;e.jhagurmukhi=2589;e.jheharmenian=1403;e.jis=12292;e.jmonospace=65354;e.jparen=9381;e.jsuperior=690;e.k=107;e.kabashkircyrillic=1185;e.kabengali=2453;e.kacute=7729;e.kacyrillic=1082;e.kadescendercyrillic=1179;e.kadeva=2325;e.kaf=1499;e.kafarabic=1603;e.kafdagesh=64315;e.kafdageshhebrew=64315;e.kaffinalarabic=65242;e.kafhebrew=1499;e.kafinitialarabic=65243;e.kafmedialarabic=65244;e.kafrafehebrew=64333;e.kagujarati=2709;e.kagurmukhi=2581;e.kahiragana=12363;e.kahookcyrillic=1220;e.kakatakana=12459;e.kakatakanahalfwidth=65398;e.kappa=954;e.kappasymbolgreek=1008;e.kapyeounmieumkorean=12657;e.kapyeounphieuphkorean=12676;e.kapyeounpieupkorean=12664;e.kapyeounssangpieupkorean=12665;e.karoriisquare=13069;e.kashidaautoarabic=1600;e.kashidaautonosidebearingarabic=1600;e.kasmallkatakana=12533;e.kasquare=13188;e.kasraarabic=1616;e.kasratanarabic=1613;e.kastrokecyrillic=1183;e.katahiraprolongmarkhalfwidth=65392;e.kaverticalstrokecyrillic=1181;e.kbopomofo=12558;e.kcalsquare=13193;e.kcaron=489;e.kcedilla=311;e.kcircle=9434;e.kcommaaccent=311;e.kdotbelow=7731;e.keharmenian=1412;e.kehiragana=12369;e.kekatakana=12465;e.kekatakanahalfwidth=65401;e.kenarmenian=1391;e.kesmallkatakana=12534;e.kgreenlandic=312;e.khabengali=2454;e.khacyrillic=1093;e.khadeva=2326;e.khagujarati=2710;e.khagurmukhi=2582;e.khaharabic=1582;e.khahfinalarabic=65190;e.khahinitialarabic=65191;e.khahmedialarabic=65192;e.kheicoptic=999;e.khhadeva=2393;e.khhagurmukhi=2649;e.khieukhacirclekorean=12920;e.khieukhaparenkorean=12824;e.khieukhcirclekorean=12906;e.khieukhkorean=12619;e.khieukhparenkorean=12810;e.khokhaithai=3586;e.khokhonthai=3589;e.khokhuatthai=3587;e.khokhwaithai=3588;e.khomutthai=3675;e.khook=409;e.khorakhangthai=3590;e.khzsquare=13201;e.kihiragana=12365;e.kikatakana=12461;e.kikatakanahalfwidth=65399;e.kiroguramusquare=13077;e.kiromeetorusquare=13078;e.kirosquare=13076;e.kiyeokacirclekorean=12910;e.kiyeokaparenkorean=12814;e.kiyeokcirclekorean=12896;e.kiyeokkorean=12593;e.kiyeokparenkorean=12800;e.kiyeoksioskorean=12595;e.kjecyrillic=1116;e.klinebelow=7733;e.klsquare=13208;e.kmcubedsquare=13222;e.kmonospace=65355;e.kmsquaredsquare=13218;e.kohiragana=12371;e.kohmsquare=13248;e.kokaithai=3585;e.kokatakana=12467;e.kokatakanahalfwidth=65402;e.kooposquare=13086;e.koppacyrillic=1153;e.koreanstandardsymbol=12927;e.koroniscmb=835;e.kparen=9382;e.kpasquare=13226;e.ksicyrillic=1135;e.ktsquare=13263;e.kturned=670;e.kuhiragana=12367;e.kukatakana=12463;e.kukatakanahalfwidth=65400;e.kvsquare=13240;e.kwsquare=13246;e.l=108;e.labengali=2482;e.lacute=314;e.ladeva=2354;e.lagujarati=2738;e.lagurmukhi=2610;e.lakkhangyaothai=3653;e.lamaleffinalarabic=65276;e.lamalefhamzaabovefinalarabic=65272;e.lamalefhamzaaboveisolatedarabic=65271;e.lamalefhamzabelowfinalarabic=65274;e.lamalefhamzabelowisolatedarabic=65273;e.lamalefisolatedarabic=65275;e.lamalefmaddaabovefinalarabic=65270;e.lamalefmaddaaboveisolatedarabic=65269;e.lamarabic=1604;e.lambda=955;e.lambdastroke=411;e.lamed=1500;e.lameddagesh=64316;e.lameddageshhebrew=64316;e.lamedhebrew=1500;e.lamfinalarabic=65246;e.lamhahinitialarabic=64714;e.laminitialarabic=65247;e.lamjeeminitialarabic=64713;e.lamkhahinitialarabic=64715;e.lamlamhehisolatedarabic=65010;e.lammedialarabic=65248;e.lammeemhahinitialarabic=64904;e.lammeeminitialarabic=64716;e.largecircle=9711;e.lbar=410;e.lbelt=620;e.lbopomofo=12556;e.lcaron=318;e.lcedilla=316;e.lcircle=9435;e.lcircumflexbelow=7741;e.lcommaaccent=316;e.ldot=320;e.ldotaccent=320;e.ldotbelow=7735;e.ldotbelowmacron=7737;e.leftangleabovecmb=794;e.lefttackbelowcmb=792;e.less=60;e.lessequal=8804;e.lessequalorgreater=8922;e.lessmonospace=65308;e.lessorequivalent=8818;e.lessorgreater=8822;e.lessoverequal=8806;e.lesssmall=65124;e.lezh=622;e.lfblock=9612;e.lhookretroflex=621;e.lira=8356;e.liwnarmenian=1388;e.lj=457;e.ljecyrillic=1113;e.ll=63168;e.lladeva=2355;e.llagujarati=2739;e.llinebelow=7739;e.llladeva=2356;e.llvocalicbengali=2529;e.llvocalicdeva=2401;e.llvocalicvowelsignbengali=2531;e.llvocalicvowelsigndeva=2403;e.lmiddletilde=619;e.lmonospace=65356;e.lmsquare=13264;e.lochulathai=3628;e.logicaland=8743;e.logicalnot=172;e.logicalnotreversed=8976;e.logicalor=8744;e.lolingthai=3621;e.longs=383;e.lowlinecenterline=65102;e.lowlinecmb=818;e.lowlinedashed=65101;e.lozenge=9674;e.lparen=9383;e.lslash=322;e.lsquare=8467;e.lsuperior=63214;e.ltshade=9617;e.luthai=3622;e.lvocalicbengali=2444;e.lvocalicdeva=2316;e.lvocalicvowelsignbengali=2530;e.lvocalicvowelsigndeva=2402;e.lxsquare=13267;e.m=109;e.mabengali=2478;e.macron=175;e.macronbelowcmb=817;e.macroncmb=772;e.macronlowmod=717;e.macronmonospace=65507;e.macute=7743;e.madeva=2350;e.magujarati=2734;e.magurmukhi=2606;e.mahapakhhebrew=1444;e.mahapakhlefthebrew=1444;e.mahiragana=12414;e.maichattawalowleftthai=63637;e.maichattawalowrightthai=63636;e.maichattawathai=3659;e.maichattawaupperleftthai=63635;e.maieklowleftthai=63628;e.maieklowrightthai=63627;e.maiekthai=3656;e.maiekupperleftthai=63626;e.maihanakatleftthai=63620;e.maihanakatthai=3633;e.maitaikhuleftthai=63625;e.maitaikhuthai=3655;e.maitholowleftthai=63631;e.maitholowrightthai=63630;e.maithothai=3657;e.maithoupperleftthai=63629;e.maitrilowleftthai=63634;e.maitrilowrightthai=63633;e.maitrithai=3658;e.maitriupperleftthai=63632;e.maiyamokthai=3654;e.makatakana=12510;e.makatakanahalfwidth=65423;e.male=9794;e.mansyonsquare=13127;e.maqafhebrew=1470;e.mars=9794;e.masoracirclehebrew=1455;e.masquare=13187;e.mbopomofo=12551;e.mbsquare=13268;e.mcircle=9436;e.mcubedsquare=13221;e.mdotaccent=7745;e.mdotbelow=7747;e.meemarabic=1605;e.meemfinalarabic=65250;e.meeminitialarabic=65251;e.meemmedialarabic=65252;e.meemmeeminitialarabic=64721;e.meemmeemisolatedarabic=64584;e.meetorusquare=13133;e.mehiragana=12417;e.meizierasquare=13182;e.mekatakana=12513;e.mekatakanahalfwidth=65426;e.mem=1502;e.memdagesh=64318;e.memdageshhebrew=64318;e.memhebrew=1502;e.menarmenian=1396;e.merkhahebrew=1445;e.merkhakefulahebrew=1446;e.merkhakefulalefthebrew=1446;e.merkhalefthebrew=1445;e.mhook=625;e.mhzsquare=13202;e.middledotkatakanahalfwidth=65381;e.middot=183;e.mieumacirclekorean=12914;e.mieumaparenkorean=12818;e.mieumcirclekorean=12900;e.mieumkorean=12609;e.mieumpansioskorean=12656;e.mieumparenkorean=12804;e.mieumpieupkorean=12654;e.mieumsioskorean=12655;e.mihiragana=12415;e.mikatakana=12511;e.mikatakanahalfwidth=65424;e.minus=8722;e.minusbelowcmb=800;e.minuscircle=8854;e.minusmod=727;e.minusplus=8723;e.minute=8242;e.miribaarusquare=13130;e.mirisquare=13129;e.mlonglegturned=624;e.mlsquare=13206;e.mmcubedsquare=13219;e.mmonospace=65357;e.mmsquaredsquare=13215;e.mohiragana=12418;e.mohmsquare=13249;e.mokatakana=12514;e.mokatakanahalfwidth=65427;e.molsquare=13270;e.momathai=3617;e.moverssquare=13223;e.moverssquaredsquare=13224;e.mparen=9384;e.mpasquare=13227;e.mssquare=13235;e.msuperior=63215;e.mturned=623;e.mu=181;e.mu1=181;e.muasquare=13186;e.muchgreater=8811;e.muchless=8810;e.mufsquare=13196;e.mugreek=956;e.mugsquare=13197;e.muhiragana=12416;e.mukatakana=12512;e.mukatakanahalfwidth=65425;e.mulsquare=13205;e.multiply=215;e.mumsquare=13211;e.munahhebrew=1443;e.munahlefthebrew=1443;e.musicalnote=9834;e.musicalnotedbl=9835;e.musicflatsign=9837;e.musicsharpsign=9839;e.mussquare=13234;e.muvsquare=13238;e.muwsquare=13244;e.mvmegasquare=13241;e.mvsquare=13239;e.mwmegasquare=13247;e.mwsquare=13245;e.n=110;e.nabengali=2472;e.nabla=8711;e.nacute=324;e.nadeva=2344;e.nagujarati=2728;e.nagurmukhi=2600;e.nahiragana=12394;e.nakatakana=12490;e.nakatakanahalfwidth=65413;e.napostrophe=329;e.nasquare=13185;e.nbopomofo=12555;e.nbspace=160;e.ncaron=328;e.ncedilla=326;e.ncircle=9437;e.ncircumflexbelow=7755;e.ncommaaccent=326;e.ndotaccent=7749;e.ndotbelow=7751;e.nehiragana=12397;e.nekatakana=12493;e.nekatakanahalfwidth=65416;e.newsheqelsign=8362;e.nfsquare=13195;e.ngabengali=2457;e.ngadeva=2329;e.ngagujarati=2713;e.ngagurmukhi=2585;e.ngonguthai=3591;e.nhiragana=12435;e.nhookleft=626;e.nhookretroflex=627;e.nieunacirclekorean=12911;e.nieunaparenkorean=12815;e.nieuncieuckorean=12597;e.nieuncirclekorean=12897;e.nieunhieuhkorean=12598;e.nieunkorean=12596;e.nieunpansioskorean=12648;e.nieunparenkorean=12801;e.nieunsioskorean=12647;e.nieuntikeutkorean=12646;e.nihiragana=12395;e.nikatakana=12491;e.nikatakanahalfwidth=65414;e.nikhahitleftthai=63641;e.nikhahitthai=3661;e.nine=57;e.ninearabic=1641;e.ninebengali=2543;e.ninecircle=9320;e.ninecircleinversesansserif=10130;e.ninedeva=2415;e.ninegujarati=2799;e.ninegurmukhi=2671;e.ninehackarabic=1641;e.ninehangzhou=12329;e.nineideographicparen=12840;e.nineinferior=8329;e.ninemonospace=65305;e.nineoldstyle=63289;e.nineparen=9340;e.nineperiod=9360;e.ninepersian=1785;e.nineroman=8568;e.ninesuperior=8313;e.nineteencircle=9330;e.nineteenparen=9350;e.nineteenperiod=9370;e.ninethai=3673;e.nj=460;e.njecyrillic=1114;e.nkatakana=12531;e.nkatakanahalfwidth=65437;e.nlegrightlong=414;e.nlinebelow=7753;e.nmonospace=65358;e.nmsquare=13210;e.nnabengali=2467;e.nnadeva=2339;e.nnagujarati=2723;e.nnagurmukhi=2595;e.nnnadeva=2345;e.nohiragana=12398;e.nokatakana=12494;e.nokatakanahalfwidth=65417;e.nonbreakingspace=160;e.nonenthai=3603;e.nonuthai=3609;e.noonarabic=1606;e.noonfinalarabic=65254;e.noonghunnaarabic=1722;e.noonghunnafinalarabic=64415;e.nooninitialarabic=65255;e.noonjeeminitialarabic=64722;e.noonjeemisolatedarabic=64587;e.noonmedialarabic=65256;e.noonmeeminitialarabic=64725;e.noonmeemisolatedarabic=64590;e.noonnoonfinalarabic=64653;e.notcontains=8716;e.notelement=8713;e.notelementof=8713;e.notequal=8800;e.notgreater=8815;e.notgreaternorequal=8817;e.notgreaternorless=8825;e.notidentical=8802;e.notless=8814;e.notlessnorequal=8816;e.notparallel=8742;e.notprecedes=8832;e.notsubset=8836;e.notsucceeds=8833;e.notsuperset=8837;e.nowarmenian=1398;e.nparen=9385;e.nssquare=13233;e.nsuperior=8319;e.ntilde=241;e.nu=957;e.nuhiragana=12396;e.nukatakana=12492;e.nukatakanahalfwidth=65415;e.nuktabengali=2492;e.nuktadeva=2364;e.nuktagujarati=2748;e.nuktagurmukhi=2620;e.numbersign=35;e.numbersignmonospace=65283;e.numbersignsmall=65119;e.numeralsigngreek=884;e.numeralsignlowergreek=885;e.numero=8470;e.nun=1504;e.nundagesh=64320;e.nundageshhebrew=64320;e.nunhebrew=1504;e.nvsquare=13237;e.nwsquare=13243;e.nyabengali=2462;e.nyadeva=2334;e.nyagujarati=2718;e.nyagurmukhi=2590;e.o=111;e.oacute=243;e.oangthai=3629;e.obarred=629;e.obarredcyrillic=1257;e.obarreddieresiscyrillic=1259;e.obengali=2451;e.obopomofo=12571;e.obreve=335;e.ocandradeva=2321;e.ocandragujarati=2705;e.ocandravowelsigndeva=2377;e.ocandravowelsigngujarati=2761;e.ocaron=466;e.ocircle=9438;e.ocircumflex=244;e.ocircumflexacute=7889;e.ocircumflexdotbelow=7897;e.ocircumflexgrave=7891;e.ocircumflexhookabove=7893;e.ocircumflextilde=7895;e.ocyrillic=1086;e.odblacute=337;e.odblgrave=525;e.odeva=2323;e.odieresis=246;e.odieresiscyrillic=1255;e.odotbelow=7885;e.oe=339;e.oekorean=12634;e.ogonek=731;e.ogonekcmb=808;e.ograve=242;e.ogujarati=2707;e.oharmenian=1413;e.ohiragana=12362;e.ohookabove=7887;e.ohorn=417;e.ohornacute=7899;e.ohorndotbelow=7907;e.ohorngrave=7901;e.ohornhookabove=7903;e.ohorntilde=7905;e.ohungarumlaut=337;e.oi=419;e.oinvertedbreve=527;e.okatakana=12458;e.okatakanahalfwidth=65397;e.okorean=12631;e.olehebrew=1451;e.omacron=333;e.omacronacute=7763;e.omacrongrave=7761;e.omdeva=2384;e.omega=969;e.omega1=982;e.omegacyrillic=1121;e.omegalatinclosed=631;e.omegaroundcyrillic=1147;e.omegatitlocyrillic=1149;e.omegatonos=974;e.omgujarati=2768;e.omicron=959;e.omicrontonos=972;e.omonospace=65359;e.one=49;e.onearabic=1633;e.onebengali=2535;e.onecircle=9312;e.onecircleinversesansserif=10122;e.onedeva=2407;e.onedotenleader=8228;e.oneeighth=8539;e.onefitted=63196;e.onegujarati=2791;e.onegurmukhi=2663;e.onehackarabic=1633;e.onehalf=189;e.onehangzhou=12321;e.oneideographicparen=12832;e.oneinferior=8321;e.onemonospace=65297;e.onenumeratorbengali=2548;e.oneoldstyle=63281;e.oneparen=9332;e.oneperiod=9352;e.onepersian=1777;e.onequarter=188;e.oneroman=8560;e.onesuperior=185;e.onethai=3665;e.onethird=8531;e.oogonek=491;e.oogonekmacron=493;e.oogurmukhi=2579;e.oomatragurmukhi=2635;e.oopen=596;e.oparen=9386;e.openbullet=9702;e.option=8997;e.ordfeminine=170;e.ordmasculine=186;e.orthogonal=8735;e.oshortdeva=2322;e.oshortvowelsigndeva=2378;e.oslash=248;e.oslashacute=511;e.osmallhiragana=12361;e.osmallkatakana=12457;e.osmallkatakanahalfwidth=65387;e.ostrokeacute=511;e.osuperior=63216;e.otcyrillic=1151;e.otilde=245;e.otildeacute=7757;e.otildedieresis=7759;e.oubopomofo=12577;e.overline=8254;e.overlinecenterline=65098;e.overlinecmb=773;e.overlinedashed=65097;e.overlinedblwavy=65100;e.overlinewavy=65099;e.overscore=175;e.ovowelsignbengali=2507;e.ovowelsigndeva=2379;e.ovowelsigngujarati=2763;e.p=112;e.paampssquare=13184;e.paasentosquare=13099;e.pabengali=2474;e.pacute=7765;e.padeva=2346;e.pagedown=8671;e.pageup=8670;e.pagujarati=2730;e.pagurmukhi=2602;e.pahiragana=12401;e.paiyannoithai=3631;e.pakatakana=12497;e.palatalizationcyrilliccmb=1156;e.palochkacyrillic=1216;e.pansioskorean=12671;e.paragraph=182;e.parallel=8741;e.parenleft=40;e.parenleftaltonearabic=64830;e.parenleftbt=63725;e.parenleftex=63724;e.parenleftinferior=8333;e.parenleftmonospace=65288;e.parenleftsmall=65113;e.parenleftsuperior=8317;e.parenlefttp=63723;e.parenleftvertical=65077;e.parenright=41;e.parenrightaltonearabic=64831;e.parenrightbt=63736;e.parenrightex=63735;e.parenrightinferior=8334;e.parenrightmonospace=65289;e.parenrightsmall=65114;e.parenrightsuperior=8318;e.parenrighttp=63734;e.parenrightvertical=65078;e.partialdiff=8706;e.paseqhebrew=1472;e.pashtahebrew=1433;e.pasquare=13225;e.patah=1463;e.patah11=1463;e.patah1d=1463;e.patah2a=1463;e.patahhebrew=1463;e.patahnarrowhebrew=1463;e.patahquarterhebrew=1463;e.patahwidehebrew=1463;e.pazerhebrew=1441;e.pbopomofo=12550;e.pcircle=9439;e.pdotaccent=7767;e.pe=1508;e.pecyrillic=1087;e.pedagesh=64324;e.pedageshhebrew=64324;e.peezisquare=13115;e.pefinaldageshhebrew=64323;e.peharabic=1662;e.peharmenian=1402;e.pehebrew=1508;e.pehfinalarabic=64343;e.pehinitialarabic=64344;e.pehiragana=12410;e.pehmedialarabic=64345;e.pekatakana=12506;e.pemiddlehookcyrillic=1191;e.perafehebrew=64334;e.percent=37;e.percentarabic=1642;e.percentmonospace=65285;e.percentsmall=65130;e.period=46;e.periodarmenian=1417;e.periodcentered=183;e.periodhalfwidth=65377;e.periodinferior=63207;e.periodmonospace=65294;e.periodsmall=65106;e.periodsuperior=63208;e.perispomenigreekcmb=834;e.perpendicular=8869;e.perthousand=8240;e.peseta=8359;e.pfsquare=13194;e.phabengali=2475;e.phadeva=2347;e.phagujarati=2731;e.phagurmukhi=2603;e.phi=966;e.phi1=981;e.phieuphacirclekorean=12922;e.phieuphaparenkorean=12826;e.phieuphcirclekorean=12908;e.phieuphkorean=12621;e.phieuphparenkorean=12812;e.philatin=632;e.phinthuthai=3642;e.phisymbolgreek=981;e.phook=421;e.phophanthai=3614;e.phophungthai=3612;e.phosamphaothai=3616;e.pi=960;e.pieupacirclekorean=12915;e.pieupaparenkorean=12819;e.pieupcieuckorean=12662;e.pieupcirclekorean=12901;e.pieupkiyeokkorean=12658;e.pieupkorean=12610;e.pieupparenkorean=12805;e.pieupsioskiyeokkorean=12660;e.pieupsioskorean=12612;e.pieupsiostikeutkorean=12661;e.pieupthieuthkorean=12663;e.pieuptikeutkorean=12659;e.pihiragana=12404;e.pikatakana=12500;e.pisymbolgreek=982;e.piwrarmenian=1411;e.planckover2pi=8463;e.planckover2pi1=8463;e.plus=43;e.plusbelowcmb=799;e.pluscircle=8853;e.plusminus=177;e.plusmod=726;e.plusmonospace=65291;e.plussmall=65122;e.plussuperior=8314;e.pmonospace=65360;e.pmsquare=13272;e.pohiragana=12413;e.pointingindexdownwhite=9759;e.pointingindexleftwhite=9756;e.pointingindexrightwhite=9758;e.pointingindexupwhite=9757;e.pokatakana=12509;e.poplathai=3611;e.postalmark=12306;e.postalmarkface=12320;e.pparen=9387;e.precedes=8826;e.prescription=8478;e.primemod=697;e.primereversed=8245;e.product=8719;e.projective=8965;e.prolongedkana=12540;e.propellor=8984;e.propersubset=8834;e.propersuperset=8835;e.proportion=8759;e.proportional=8733;e.psi=968;e.psicyrillic=1137;e.psilipneumatacyrilliccmb=1158;e.pssquare=13232;e.puhiragana=12407;e.pukatakana=12503;e.pvsquare=13236;e.pwsquare=13242;e.q=113;e.qadeva=2392;e.qadmahebrew=1448;e.qafarabic=1602;e.qaffinalarabic=65238;e.qafinitialarabic=65239;e.qafmedialarabic=65240;e.qamats=1464;e.qamats10=1464;e.qamats1a=1464;e.qamats1c=1464;e.qamats27=1464;e.qamats29=1464;e.qamats33=1464;e.qamatsde=1464;e.qamatshebrew=1464;e.qamatsnarrowhebrew=1464;e.qamatsqatanhebrew=1464;e.qamatsqatannarrowhebrew=1464;e.qamatsqatanquarterhebrew=1464;e.qamatsqatanwidehebrew=1464;e.qamatsquarterhebrew=1464;e.qamatswidehebrew=1464;e.qarneyparahebrew=1439;e.qbopomofo=12561;e.qcircle=9440;e.qhook=672;e.qmonospace=65361;e.qof=1511;e.qofdagesh=64327;e.qofdageshhebrew=64327;e.qofhebrew=1511;e.qparen=9388;e.quarternote=9833;e.qubuts=1467;e.qubuts18=1467;e.qubuts25=1467;e.qubuts31=1467;e.qubutshebrew=1467;e.qubutsnarrowhebrew=1467;e.qubutsquarterhebrew=1467;e.qubutswidehebrew=1467;e.question=63;e.questionarabic=1567;e.questionarmenian=1374;e.questiondown=191;e.questiondownsmall=63423;e.questiongreek=894;e.questionmonospace=65311;e.questionsmall=63295;e.quotedbl=34;e.quotedblbase=8222;e.quotedblleft=8220;e.quotedblmonospace=65282;e.quotedblprime=12318;e.quotedblprimereversed=12317;e.quotedblright=8221;e.quoteleft=8216;e.quoteleftreversed=8219;e.quotereversed=8219;e.quoteright=8217;e.quoterightn=329;e.quotesinglbase=8218;e.quotesingle=39;e.quotesinglemonospace=65287;e.r=114;e.raarmenian=1404;e.rabengali=2480;e.racute=341;e.radeva=2352;e.radical=8730;e.radicalex=63717;e.radoverssquare=13230;e.radoverssquaredsquare=13231;e.radsquare=13229;e.rafe=1471;e.rafehebrew=1471;e.ragujarati=2736;e.ragurmukhi=2608;e.rahiragana=12425;e.rakatakana=12521;e.rakatakanahalfwidth=65431;e.ralowerdiagonalbengali=2545;e.ramiddlediagonalbengali=2544;e.ramshorn=612;e.ratio=8758;e.rbopomofo=12566;e.rcaron=345;e.rcedilla=343;e.rcircle=9441;e.rcommaaccent=343;e.rdblgrave=529;e.rdotaccent=7769;e.rdotbelow=7771;e.rdotbelowmacron=7773;e.referencemark=8251;e.reflexsubset=8838;e.reflexsuperset=8839;e.registered=174;e.registersans=63720;e.registerserif=63194;e.reharabic=1585;e.reharmenian=1408;e.rehfinalarabic=65198;e.rehiragana=12428;e.rekatakana=12524;e.rekatakanahalfwidth=65434;e.resh=1512;e.reshdageshhebrew=64328;e.reshhebrew=1512;e.reversedtilde=8765;e.reviahebrew=1431;e.reviamugrashhebrew=1431;e.revlogicalnot=8976;e.rfishhook=638;e.rfishhookreversed=639;e.rhabengali=2525;e.rhadeva=2397;e.rho=961;e.rhook=637;e.rhookturned=635;e.rhookturnedsuperior=693;e.rhosymbolgreek=1009;e.rhotichookmod=734;e.rieulacirclekorean=12913;e.rieulaparenkorean=12817;e.rieulcirclekorean=12899;e.rieulhieuhkorean=12608;e.rieulkiyeokkorean=12602;e.rieulkiyeoksioskorean=12649;e.rieulkorean=12601;e.rieulmieumkorean=12603;e.rieulpansioskorean=12652;e.rieulparenkorean=12803;e.rieulphieuphkorean=12607;e.rieulpieupkorean=12604;e.rieulpieupsioskorean=12651;e.rieulsioskorean=12605;e.rieulthieuthkorean=12606;e.rieultikeutkorean=12650;e.rieulyeorinhieuhkorean=12653;e.rightangle=8735;e.righttackbelowcmb=793;e.righttriangle=8895;e.rihiragana=12426;e.rikatakana=12522;e.rikatakanahalfwidth=65432;e.ring=730;e.ringbelowcmb=805;e.ringcmb=778;e.ringhalfleft=703;e.ringhalfleftarmenian=1369;e.ringhalfleftbelowcmb=796;e.ringhalfleftcentered=723;e.ringhalfright=702;e.ringhalfrightbelowcmb=825;e.ringhalfrightcentered=722;e.rinvertedbreve=531;e.rittorusquare=13137;e.rlinebelow=7775;e.rlongleg=636;e.rlonglegturned=634;e.rmonospace=65362;e.rohiragana=12429;e.rokatakana=12525;e.rokatakanahalfwidth=65435;e.roruathai=3619;e.rparen=9389;e.rrabengali=2524;e.rradeva=2353;e.rragurmukhi=2652;e.rreharabic=1681;e.rrehfinalarabic=64397;e.rrvocalicbengali=2528;e.rrvocalicdeva=2400;e.rrvocalicgujarati=2784;e.rrvocalicvowelsignbengali=2500;e.rrvocalicvowelsigndeva=2372;e.rrvocalicvowelsigngujarati=2756;e.rsuperior=63217;e.rtblock=9616;e.rturned=633;e.rturnedsuperior=692;e.ruhiragana=12427;e.rukatakana=12523;e.rukatakanahalfwidth=65433;e.rupeemarkbengali=2546;e.rupeesignbengali=2547;e.rupiah=63197;e.ruthai=3620;e.rvocalicbengali=2443;e.rvocalicdeva=2315;e.rvocalicgujarati=2699;e.rvocalicvowelsignbengali=2499;e.rvocalicvowelsigndeva=2371;e.rvocalicvowelsigngujarati=2755;e.s=115;e.sabengali=2488;e.sacute=347;e.sacutedotaccent=7781;e.sadarabic=1589;e.sadeva=2360;e.sadfinalarabic=65210;e.sadinitialarabic=65211;e.sadmedialarabic=65212;e.sagujarati=2744;e.sagurmukhi=2616;e.sahiragana=12373;e.sakatakana=12469;e.sakatakanahalfwidth=65403;e.sallallahoualayhewasallamarabic=65018;e.samekh=1505;e.samekhdagesh=64321;e.samekhdageshhebrew=64321;e.samekhhebrew=1505;e.saraaathai=3634;e.saraaethai=3649;e.saraaimaimalaithai=3652;e.saraaimaimuanthai=3651;e.saraamthai=3635;e.saraathai=3632;e.saraethai=3648;e.saraiileftthai=63622;e.saraiithai=3637;e.saraileftthai=63621;e.saraithai=3636;e.saraothai=3650;e.saraueeleftthai=63624;e.saraueethai=3639;e.saraueleftthai=63623;e.sarauethai=3638;e.sarauthai=3640;e.sarauuthai=3641;e.sbopomofo=12569;e.scaron=353;e.scarondotaccent=7783;e.scedilla=351;e.schwa=601;e.schwacyrillic=1241;e.schwadieresiscyrillic=1243;e.schwahook=602;e.scircle=9442;e.scircumflex=349;e.scommaaccent=537;e.sdotaccent=7777;e.sdotbelow=7779;e.sdotbelowdotaccent=7785;e.seagullbelowcmb=828;e.second=8243;e.secondtonechinese=714;e.section=167;e.seenarabic=1587;e.seenfinalarabic=65202;e.seeninitialarabic=65203;e.seenmedialarabic=65204;e.segol=1462;e.segol13=1462;e.segol1f=1462;e.segol2c=1462;e.segolhebrew=1462;e.segolnarrowhebrew=1462;e.segolquarterhebrew=1462;e.segoltahebrew=1426;e.segolwidehebrew=1462;e.seharmenian=1405;e.sehiragana=12379;e.sekatakana=12475;e.sekatakanahalfwidth=65406;e.semicolon=59;e.semicolonarabic=1563;e.semicolonmonospace=65307;e.semicolonsmall=65108;e.semivoicedmarkkana=12444;e.semivoicedmarkkanahalfwidth=65439;e.sentisquare=13090;e.sentosquare=13091;e.seven=55;e.sevenarabic=1639;e.sevenbengali=2541;e.sevencircle=9318;e.sevencircleinversesansserif=10128;e.sevendeva=2413;e.seveneighths=8542;e.sevengujarati=2797;e.sevengurmukhi=2669;e.sevenhackarabic=1639;e.sevenhangzhou=12327;e.sevenideographicparen=12838;e.seveninferior=8327;e.sevenmonospace=65303;e.sevenoldstyle=63287;e.sevenparen=9338;e.sevenperiod=9358;e.sevenpersian=1783;e.sevenroman=8566;e.sevensuperior=8311;e.seventeencircle=9328;e.seventeenparen=9348;e.seventeenperiod=9368;e.seventhai=3671;e.sfthyphen=173;e.shaarmenian=1399;e.shabengali=2486;e.shacyrillic=1096;e.shaddaarabic=1617;e.shaddadammaarabic=64609;e.shaddadammatanarabic=64606;e.shaddafathaarabic=64608;e.shaddakasraarabic=64610;e.shaddakasratanarabic=64607;e.shade=9618;e.shadedark=9619;e.shadelight=9617;e.shademedium=9618;e.shadeva=2358;e.shagujarati=2742;e.shagurmukhi=2614;e.shalshelethebrew=1427;e.shbopomofo=12565;e.shchacyrillic=1097;e.sheenarabic=1588;e.sheenfinalarabic=65206;e.sheeninitialarabic=65207;e.sheenmedialarabic=65208;e.sheicoptic=995;e.sheqel=8362;e.sheqelhebrew=8362;e.sheva=1456;e.sheva115=1456;e.sheva15=1456;e.sheva22=1456;e.sheva2e=1456;e.shevahebrew=1456;e.shevanarrowhebrew=1456;e.shevaquarterhebrew=1456;e.shevawidehebrew=1456;e.shhacyrillic=1211;e.shimacoptic=1005;e.shin=1513;e.shindagesh=64329;e.shindageshhebrew=64329;e.shindageshshindot=64300;e.shindageshshindothebrew=64300;e.shindageshsindot=64301;e.shindageshsindothebrew=64301;e.shindothebrew=1473;e.shinhebrew=1513;e.shinshindot=64298;e.shinshindothebrew=64298;e.shinsindot=64299;e.shinsindothebrew=64299;e.shook=642;e.sigma=963;e.sigma1=962;e.sigmafinal=962;e.sigmalunatesymbolgreek=1010;e.sihiragana=12375;e.sikatakana=12471;e.sikatakanahalfwidth=65404;e.siluqhebrew=1469;e.siluqlefthebrew=1469;e.similar=8764;e.sindothebrew=1474;e.siosacirclekorean=12916;e.siosaparenkorean=12820;e.sioscieuckorean=12670;e.sioscirclekorean=12902;e.sioskiyeokkorean=12666;e.sioskorean=12613;e.siosnieunkorean=12667;e.siosparenkorean=12806;e.siospieupkorean=12669;e.siostikeutkorean=12668;e.six=54;e.sixarabic=1638;e.sixbengali=2540;e.sixcircle=9317;e.sixcircleinversesansserif=10127;e.sixdeva=2412;e.sixgujarati=2796;e.sixgurmukhi=2668;e.sixhackarabic=1638;e.sixhangzhou=12326;e.sixideographicparen=12837;e.sixinferior=8326;e.sixmonospace=65302;e.sixoldstyle=63286;e.sixparen=9337;e.sixperiod=9357;e.sixpersian=1782;e.sixroman=8565;e.sixsuperior=8310;e.sixteencircle=9327;e.sixteencurrencydenominatorbengali=2553;e.sixteenparen=9347;e.sixteenperiod=9367;e.sixthai=3670;e.slash=47;e.slashmonospace=65295;e.slong=383;e.slongdotaccent=7835;e.smileface=9786;e.smonospace=65363;e.sofpasuqhebrew=1475;e.softhyphen=173;e.softsigncyrillic=1100;e.sohiragana=12381;e.sokatakana=12477;e.sokatakanahalfwidth=65407;e.soliduslongoverlaycmb=824;e.solidusshortoverlaycmb=823;e.sorusithai=3625;e.sosalathai=3624;e.sosothai=3595;e.sosuathai=3626;e.space=32;e.spacehackarabic=32;e.spade=9824;e.spadesuitblack=9824;e.spadesuitwhite=9828;e.sparen=9390;e.squarebelowcmb=827;e.squarecc=13252;e.squarecm=13213;e.squarediagonalcrosshatchfill=9641;e.squarehorizontalfill=9636;e.squarekg=13199;e.squarekm=13214;e.squarekmcapital=13262;e.squareln=13265;e.squarelog=13266;e.squaremg=13198;e.squaremil=13269;e.squaremm=13212;e.squaremsquared=13217;e.squareorthogonalcrosshatchfill=9638;e.squareupperlefttolowerrightfill=9639;e.squareupperrighttolowerleftfill=9640;e.squareverticalfill=9637;e.squarewhitewithsmallblack=9635;e.srsquare=13275;e.ssabengali=2487;e.ssadeva=2359;e.ssagujarati=2743;e.ssangcieuckorean=12617;e.ssanghieuhkorean=12677;e.ssangieungkorean=12672;e.ssangkiyeokkorean=12594;e.ssangnieunkorean=12645;e.ssangpieupkorean=12611;e.ssangsioskorean=12614;e.ssangtikeutkorean=12600;e.ssuperior=63218;e.sterling=163;e.sterlingmonospace=65505;e.strokelongoverlaycmb=822;e.strokeshortoverlaycmb=821;e.subset=8834;e.subsetnotequal=8842;e.subsetorequal=8838;e.succeeds=8827;e.suchthat=8715;e.suhiragana=12377;e.sukatakana=12473;e.sukatakanahalfwidth=65405;e.sukunarabic=1618;e.summation=8721;e.sun=9788;e.superset=8835;e.supersetnotequal=8843;e.supersetorequal=8839;e.svsquare=13276;e.syouwaerasquare=13180;e.t=116;e.tabengali=2468;e.tackdown=8868;e.tackleft=8867;e.tadeva=2340;e.tagujarati=2724;e.tagurmukhi=2596;e.taharabic=1591;e.tahfinalarabic=65218;e.tahinitialarabic=65219;e.tahiragana=12383;e.tahmedialarabic=65220;e.taisyouerasquare=13181;e.takatakana=12479;e.takatakanahalfwidth=65408;e.tatweelarabic=1600;e.tau=964;e.tav=1514;e.tavdages=64330;e.tavdagesh=64330;e.tavdageshhebrew=64330;e.tavhebrew=1514;e.tbar=359;e.tbopomofo=12554;e.tcaron=357;e.tccurl=680;e.tcedilla=355;e.tcheharabic=1670;e.tchehfinalarabic=64379;e.tchehinitialarabic=64380;e.tchehmedialarabic=64381;e.tcircle=9443;e.tcircumflexbelow=7793;e.tcommaaccent=355;e.tdieresis=7831;e.tdotaccent=7787;e.tdotbelow=7789;e.tecyrillic=1090;e.tedescendercyrillic=1197;e.teharabic=1578;e.tehfinalarabic=65174;e.tehhahinitialarabic=64674;e.tehhahisolatedarabic=64524;e.tehinitialarabic=65175;e.tehiragana=12390;e.tehjeeminitialarabic=64673;e.tehjeemisolatedarabic=64523;e.tehmarbutaarabic=1577;e.tehmarbutafinalarabic=65172;e.tehmedialarabic=65176;e.tehmeeminitialarabic=64676;e.tehmeemisolatedarabic=64526;e.tehnoonfinalarabic=64627;e.tekatakana=12486;e.tekatakanahalfwidth=65411;e.telephone=8481;e.telephoneblack=9742;e.telishagedolahebrew=1440;e.telishaqetanahebrew=1449;e.tencircle=9321;e.tenideographicparen=12841;e.tenparen=9341;e.tenperiod=9361;e.tenroman=8569;e.tesh=679;e.tet=1496;e.tetdagesh=64312;e.tetdageshhebrew=64312;e.tethebrew=1496;e.tetsecyrillic=1205;e.tevirhebrew=1435;e.tevirlefthebrew=1435;e.thabengali=2469;e.thadeva=2341;e.thagujarati=2725;e.thagurmukhi=2597;e.thalarabic=1584;e.thalfinalarabic=65196;e.thanthakhatlowleftthai=63640;e.thanthakhatlowrightthai=63639;e.thanthakhatthai=3660;e.thanthakhatupperleftthai=63638;e.theharabic=1579;e.thehfinalarabic=65178;e.thehinitialarabic=65179;e.thehmedialarabic=65180;e.thereexists=8707;e.therefore=8756;e.theta=952;e.theta1=977;e.thetasymbolgreek=977;e.thieuthacirclekorean=12921;e.thieuthaparenkorean=12825;e.thieuthcirclekorean=12907;e.thieuthkorean=12620;e.thieuthparenkorean=12811;e.thirteencircle=9324;e.thirteenparen=9344;e.thirteenperiod=9364;e.thonangmonthothai=3601;e.thook=429;e.thophuthaothai=3602;e.thorn=254;e.thothahanthai=3607;e.thothanthai=3600;e.thothongthai=3608;e.thothungthai=3606;e.thousandcyrillic=1154;e.thousandsseparatorarabic=1644;e.thousandsseparatorpersian=1644;e.three=51;e.threearabic=1635;e.threebengali=2537;e.threecircle=9314;e.threecircleinversesansserif=10124;e.threedeva=2409;e.threeeighths=8540;e.threegujarati=2793;e.threegurmukhi=2665;e.threehackarabic=1635;e.threehangzhou=12323;e.threeideographicparen=12834;e.threeinferior=8323;e.threemonospace=65299;e.threenumeratorbengali=2550;e.threeoldstyle=63283;e.threeparen=9334;e.threeperiod=9354;e.threepersian=1779;e.threequarters=190;e.threequartersemdash=63198;e.threeroman=8562;e.threesuperior=179;e.threethai=3667;e.thzsquare=13204;e.tihiragana=12385;e.tikatakana=12481;e.tikatakanahalfwidth=65409;e.tikeutacirclekorean=12912;e.tikeutaparenkorean=12816;e.tikeutcirclekorean=12898;e.tikeutkorean=12599;e.tikeutparenkorean=12802;e.tilde=732;e.tildebelowcmb=816;e.tildecmb=771;e.tildecomb=771;e.tildedoublecmb=864;e.tildeoperator=8764;e.tildeoverlaycmb=820;e.tildeverticalcmb=830;e.timescircle=8855;e.tipehahebrew=1430;e.tipehalefthebrew=1430;e.tippigurmukhi=2672;e.titlocyrilliccmb=1155;e.tiwnarmenian=1407;e.tlinebelow=7791;e.tmonospace=65364;e.toarmenian=1385;e.tohiragana=12392;e.tokatakana=12488;e.tokatakanahalfwidth=65412;e.tonebarextrahighmod=741;e.tonebarextralowmod=745;e.tonebarhighmod=742;e.tonebarlowmod=744;e.tonebarmidmod=743;e.tonefive=445;e.tonesix=389;e.tonetwo=424;e.tonos=900;e.tonsquare=13095;e.topatakthai=3599;e.tortoiseshellbracketleft=12308;e.tortoiseshellbracketleftsmall=65117;e.tortoiseshellbracketleftvertical=65081;e.tortoiseshellbracketright=12309;e.tortoiseshellbracketrightsmall=65118;e.tortoiseshellbracketrightvertical=65082;e.totaothai=3605;e.tpalatalhook=427;e.tparen=9391;e.trademark=8482;e.trademarksans=63722;e.trademarkserif=63195;e.tretroflexhook=648;e.triagdn=9660;e.triaglf=9668;e.triagrt=9658;e.triagup=9650;e.ts=678;e.tsadi=1510;e.tsadidagesh=64326;e.tsadidageshhebrew=64326;e.tsadihebrew=1510;e.tsecyrillic=1094;e.tsere=1461;e.tsere12=1461;e.tsere1e=1461;e.tsere2b=1461;e.tserehebrew=1461;e.tserenarrowhebrew=1461;e.tserequarterhebrew=1461;e.tserewidehebrew=1461;e.tshecyrillic=1115;e.tsuperior=63219;e.ttabengali=2463;e.ttadeva=2335;e.ttagujarati=2719;e.ttagurmukhi=2591;e.tteharabic=1657;e.ttehfinalarabic=64359;e.ttehinitialarabic=64360;e.ttehmedialarabic=64361;e.tthabengali=2464;e.tthadeva=2336;e.tthagujarati=2720;e.tthagurmukhi=2592;e.tturned=647;e.tuhiragana=12388;e.tukatakana=12484;e.tukatakanahalfwidth=65410;e.tusmallhiragana=12387;e.tusmallkatakana=12483;e.tusmallkatakanahalfwidth=65391;e.twelvecircle=9323;e.twelveparen=9343;e.twelveperiod=9363;e.twelveroman=8571;e.twentycircle=9331;e.twentyhangzhou=21316;e.twentyparen=9351;e.twentyperiod=9371;e.two=50;e.twoarabic=1634;e.twobengali=2536;e.twocircle=9313;e.twocircleinversesansserif=10123;e.twodeva=2408;e.twodotenleader=8229;e.twodotleader=8229;e.twodotleadervertical=65072;e.twogujarati=2792;e.twogurmukhi=2664;e.twohackarabic=1634;e.twohangzhou=12322;e.twoideographicparen=12833;e.twoinferior=8322;e.twomonospace=65298;e.twonumeratorbengali=2549;e.twooldstyle=63282;e.twoparen=9333;e.twoperiod=9353;e.twopersian=1778;e.tworoman=8561;e.twostroke=443;e.twosuperior=178;e.twothai=3666;e.twothirds=8532;e.u=117;e.uacute=250;e.ubar=649;e.ubengali=2441;e.ubopomofo=12584;e.ubreve=365;e.ucaron=468;e.ucircle=9444;e.ucircumflex=251;e.ucircumflexbelow=7799;e.ucyrillic=1091;e.udattadeva=2385;e.udblacute=369;e.udblgrave=533;e.udeva=2313;e.udieresis=252;e.udieresisacute=472;e.udieresisbelow=7795;e.udieresiscaron=474;e.udieresiscyrillic=1265;e.udieresisgrave=476;e.udieresismacron=470;e.udotbelow=7909;e.ugrave=249;e.ugujarati=2697;e.ugurmukhi=2569;e.uhiragana=12358;e.uhookabove=7911;e.uhorn=432;e.uhornacute=7913;e.uhorndotbelow=7921;e.uhorngrave=7915;e.uhornhookabove=7917;e.uhorntilde=7919;e.uhungarumlaut=369;e.uhungarumlautcyrillic=1267;e.uinvertedbreve=535;e.ukatakana=12454;e.ukatakanahalfwidth=65395;e.ukcyrillic=1145;e.ukorean=12636;e.umacron=363;e.umacroncyrillic=1263;e.umacrondieresis=7803;e.umatragurmukhi=2625;e.umonospace=65365;e.underscore=95;e.underscoredbl=8215;e.underscoremonospace=65343;e.underscorevertical=65075;e.underscorewavy=65103;e.union=8746;e.universal=8704;e.uogonek=371;e.uparen=9392;e.upblock=9600;e.upperdothebrew=1476;e.upsilon=965;e.upsilondieresis=971;e.upsilondieresistonos=944;e.upsilonlatin=650;e.upsilontonos=973;e.uptackbelowcmb=797;e.uptackmod=724;e.uragurmukhi=2675;e.uring=367;e.ushortcyrillic=1118;e.usmallhiragana=12357;e.usmallkatakana=12453;e.usmallkatakanahalfwidth=65385;e.ustraightcyrillic=1199;e.ustraightstrokecyrillic=1201;e.utilde=361;e.utildeacute=7801;e.utildebelow=7797;e.uubengali=2442;e.uudeva=2314;e.uugujarati=2698;e.uugurmukhi=2570;e.uumatragurmukhi=2626;e.uuvowelsignbengali=2498;e.uuvowelsigndeva=2370;e.uuvowelsigngujarati=2754;e.uvowelsignbengali=2497;e.uvowelsigndeva=2369;e.uvowelsigngujarati=2753;e.v=118;e.vadeva=2357;e.vagujarati=2741;e.vagurmukhi=2613;e.vakatakana=12535;e.vav=1493;e.vavdagesh=64309;e.vavdagesh65=64309;e.vavdageshhebrew=64309;e.vavhebrew=1493;e.vavholam=64331;e.vavholamhebrew=64331;e.vavvavhebrew=1520;e.vavyodhebrew=1521;e.vcircle=9445;e.vdotbelow=7807;e.vecyrillic=1074;e.veharabic=1700;e.vehfinalarabic=64363;e.vehinitialarabic=64364;e.vehmedialarabic=64365;e.vekatakana=12537;e.venus=9792;e.verticalbar=124;e.verticallineabovecmb=781;e.verticallinebelowcmb=809;e.verticallinelowmod=716;e.verticallinemod=712;e.vewarmenian=1406;e.vhook=651;e.vikatakana=12536;e.viramabengali=2509;e.viramadeva=2381;e.viramagujarati=2765;e.visargabengali=2435;e.visargadeva=2307;e.visargagujarati=2691;e.vmonospace=65366;e.voarmenian=1400;e.voicediterationhiragana=12446;e.voicediterationkatakana=12542;e.voicedmarkkana=12443;e.voicedmarkkanahalfwidth=65438;e.vokatakana=12538;e.vparen=9393;e.vtilde=7805;e.vturned=652;e.vuhiragana=12436;e.vukatakana=12532;e.w=119;e.wacute=7811;e.waekorean=12633;e.wahiragana=12431;e.wakatakana=12527;e.wakatakanahalfwidth=65436;e.wakorean=12632;e.wasmallhiragana=12430;e.wasmallkatakana=12526;e.wattosquare=13143;e.wavedash=12316;e.wavyunderscorevertical=65076;e.wawarabic=1608;e.wawfinalarabic=65262;e.wawhamzaabovearabic=1572;e.wawhamzaabovefinalarabic=65158;e.wbsquare=13277;e.wcircle=9446;e.wcircumflex=373;e.wdieresis=7813;e.wdotaccent=7815;e.wdotbelow=7817;e.wehiragana=12433;e.weierstrass=8472;e.wekatakana=12529;e.wekorean=12638;e.weokorean=12637;e.wgrave=7809;e.whitebullet=9702;e.whitecircle=9675;e.whitecircleinverse=9689;e.whitecornerbracketleft=12302;e.whitecornerbracketleftvertical=65091;e.whitecornerbracketright=12303;e.whitecornerbracketrightvertical=65092;e.whitediamond=9671;e.whitediamondcontainingblacksmalldiamond=9672;e.whitedownpointingsmalltriangle=9663;e.whitedownpointingtriangle=9661;e.whiteleftpointingsmalltriangle=9667;e.whiteleftpointingtriangle=9665;e.whitelenticularbracketleft=12310;e.whitelenticularbracketright=12311;e.whiterightpointingsmalltriangle=9657;e.whiterightpointingtriangle=9655;e.whitesmallsquare=9643;e.whitesmilingface=9786;e.whitesquare=9633;e.whitestar=9734;e.whitetelephone=9743;e.whitetortoiseshellbracketleft=12312;e.whitetortoiseshellbracketright=12313;e.whiteuppointingsmalltriangle=9653;e.whiteuppointingtriangle=9651;e.wihiragana=12432;e.wikatakana=12528;e.wikorean=12639;e.wmonospace=65367;e.wohiragana=12434;e.wokatakana=12530;e.wokatakanahalfwidth=65382;e.won=8361;e.wonmonospace=65510;e.wowaenthai=3623;e.wparen=9394;e.wring=7832;e.wsuperior=695;e.wturned=653;e.wynn=447;e.x=120;e.xabovecmb=829;e.xbopomofo=12562;e.xcircle=9447;e.xdieresis=7821;e.xdotaccent=7819;e.xeharmenian=1389;e.xi=958;e.xmonospace=65368;e.xparen=9395;e.xsuperior=739;e.y=121;e.yaadosquare=13134;e.yabengali=2479;e.yacute=253;e.yadeva=2351;e.yaekorean=12626;e.yagujarati=2735;e.yagurmukhi=2607;e.yahiragana=12420;e.yakatakana=12516;e.yakatakanahalfwidth=65428;e.yakorean=12625;e.yamakkanthai=3662;e.yasmallhiragana=12419;e.yasmallkatakana=12515;e.yasmallkatakanahalfwidth=65388;e.yatcyrillic=1123;e.ycircle=9448;e.ycircumflex=375;e.ydieresis=255;e.ydotaccent=7823;e.ydotbelow=7925;e.yeharabic=1610;e.yehbarreearabic=1746;e.yehbarreefinalarabic=64431;e.yehfinalarabic=65266;e.yehhamzaabovearabic=1574;e.yehhamzaabovefinalarabic=65162;e.yehhamzaaboveinitialarabic=65163;e.yehhamzaabovemedialarabic=65164;e.yehinitialarabic=65267;e.yehmedialarabic=65268;e.yehmeeminitialarabic=64733;e.yehmeemisolatedarabic=64600;e.yehnoonfinalarabic=64660;e.yehthreedotsbelowarabic=1745;e.yekorean=12630;e.yen=165;e.yenmonospace=65509;e.yeokorean=12629;e.yeorinhieuhkorean=12678;e.yerahbenyomohebrew=1450;e.yerahbenyomolefthebrew=1450;e.yericyrillic=1099;e.yerudieresiscyrillic=1273;e.yesieungkorean=12673;e.yesieungpansioskorean=12675;e.yesieungsioskorean=12674;e.yetivhebrew=1434;e.ygrave=7923;e.yhook=436;e.yhookabove=7927;e.yiarmenian=1397;e.yicyrillic=1111;e.yikorean=12642;e.yinyang=9775;e.yiwnarmenian=1410;e.ymonospace=65369;e.yod=1497;e.yoddagesh=64313;e.yoddageshhebrew=64313;e.yodhebrew=1497;e.yodyodhebrew=1522;e.yodyodpatahhebrew=64287;e.yohiragana=12424;e.yoikorean=12681;e.yokatakana=12520;e.yokatakanahalfwidth=65430;e.yokorean=12635;e.yosmallhiragana=12423;e.yosmallkatakana=12519;e.yosmallkatakanahalfwidth=65390;e.yotgreek=1011;e.yoyaekorean=12680;e.yoyakorean=12679;e.yoyakthai=3618;e.yoyingthai=3597;e.yparen=9396;e.ypogegrammeni=890;e.ypogegrammenigreekcmb=837;e.yr=422;e.yring=7833;e.ysuperior=696;e.ytilde=7929;e.yturned=654;e.yuhiragana=12422;e.yuikorean=12684;e.yukatakana=12518;e.yukatakanahalfwidth=65429;e.yukorean=12640;e.yusbigcyrillic=1131;e.yusbigiotifiedcyrillic=1133;e.yuslittlecyrillic=1127;e.yuslittleiotifiedcyrillic=1129;e.yusmallhiragana=12421;e.yusmallkatakana=12517;e.yusmallkatakanahalfwidth=65389;e.yuyekorean=12683;e.yuyeokorean=12682;e.yyabengali=2527;e.yyadeva=2399;e.z=122;e.zaarmenian=1382;e.zacute=378;e.zadeva=2395;e.zagurmukhi=2651;e.zaharabic=1592;e.zahfinalarabic=65222;e.zahinitialarabic=65223;e.zahiragana=12374;e.zahmedialarabic=65224;e.zainarabic=1586;e.zainfinalarabic=65200;e.zakatakana=12470;e.zaqefgadolhebrew=1429;e.zaqefqatanhebrew=1428;e.zarqahebrew=1432;e.zayin=1494;e.zayindagesh=64310;e.zayindageshhebrew=64310;e.zayinhebrew=1494;e.zbopomofo=12567;e.zcaron=382;e.zcircle=9449;e.zcircumflex=7825;e.zcurl=657;e.zdot=380;e.zdotaccent=380;e.zdotbelow=7827;e.zecyrillic=1079;e.zedescendercyrillic=1177;e.zedieresiscyrillic=1247;e.zehiragana=12380;e.zekatakana=12476;e.zero=48;e.zeroarabic=1632;e.zerobengali=2534;e.zerodeva=2406;e.zerogujarati=2790;e.zerogurmukhi=2662;e.zerohackarabic=1632;e.zeroinferior=8320;e.zeromonospace=65296;e.zerooldstyle=63280;e.zeropersian=1776;e.zerosuperior=8304;e.zerothai=3664;e.zerowidthjoiner=65279;e.zerowidthnonjoiner=8204;e.zerowidthspace=8203;e.zeta=950;e.zhbopomofo=12563;e.zhearmenian=1386;e.zhebrevecyrillic=1218;e.zhecyrillic=1078;e.zhedescendercyrillic=1175;e.zhedieresiscyrillic=1245;e.zihiragana=12376;e.zikatakana=12472;e.zinorhebrew=1454;e.zlinebelow=7829;e.zmonospace=65370;e.zohiragana=12382;e.zokatakana=12478;e.zparen=9397;e.zretroflexhook=656;e.zstroke=438;e.zuhiragana=12378;e.zukatakana=12474;e[".notdef"]=0;e.angbracketleftbig=9001;e.angbracketleftBig=9001;e.angbracketleftbigg=9001;e.angbracketleftBigg=9001;e.angbracketrightBig=9002;e.angbracketrightbig=9002;e.angbracketrightBigg=9002;e.angbracketrightbigg=9002;e.arrowhookleft=8618;e.arrowhookright=8617;e.arrowlefttophalf=8636;e.arrowleftbothalf=8637;e.arrownortheast=8599;e.arrownorthwest=8598;e.arrowrighttophalf=8640;e.arrowrightbothalf=8641;e.arrowsoutheast=8600;e.arrowsouthwest=8601;e.backslashbig=8726;e.backslashBig=8726;e.backslashBigg=8726;e.backslashbigg=8726;e.bardbl=8214;e.bracehtipdownleft=65079;e.bracehtipdownright=65079;e.bracehtipupleft=65080;e.bracehtipupright=65080;e.braceleftBig=123;e.braceleftbig=123;e.braceleftbigg=123;e.braceleftBigg=123;e.bracerightBig=125;e.bracerightbig=125;e.bracerightbigg=125;e.bracerightBigg=125;e.bracketleftbig=91;e.bracketleftBig=91;e.bracketleftbigg=91;e.bracketleftBigg=91;e.bracketrightBig=93;e.bracketrightbig=93;e.bracketrightbigg=93;e.bracketrightBigg=93;e.ceilingleftbig=8968;e.ceilingleftBig=8968;e.ceilingleftBigg=8968;e.ceilingleftbigg=8968;e.ceilingrightbig=8969;e.ceilingrightBig=8969;e.ceilingrightbigg=8969;e.ceilingrightBigg=8969;e.circledotdisplay=8857;e.circledottext=8857;e.circlemultiplydisplay=8855;e.circlemultiplytext=8855;e.circleplusdisplay=8853;e.circleplustext=8853;e.contintegraldisplay=8750;e.contintegraltext=8750;e.coproductdisplay=8720;e.coproducttext=8720;e.floorleftBig=8970;e.floorleftbig=8970;e.floorleftbigg=8970;e.floorleftBigg=8970;e.floorrightbig=8971;e.floorrightBig=8971;e.floorrightBigg=8971;e.floorrightbigg=8971;e.hatwide=770;e.hatwider=770;e.hatwidest=770;e.intercal=7488;e.integraldisplay=8747;e.integraltext=8747;e.intersectiondisplay=8898;e.intersectiontext=8898;e.logicalanddisplay=8743;e.logicalandtext=8743;e.logicalordisplay=8744;e.logicalortext=8744;e.parenleftBig=40;e.parenleftbig=40;e.parenleftBigg=40;e.parenleftbigg=40;e.parenrightBig=41;e.parenrightbig=41;e.parenrightBigg=41;e.parenrightbigg=41;e.prime=8242;e.productdisplay=8719;e.producttext=8719;e.radicalbig=8730;e.radicalBig=8730;e.radicalBigg=8730;e.radicalbigg=8730;e.radicalbt=8730;e.radicaltp=8730;e.radicalvertex=8730;e.slashbig=47;e.slashBig=47;e.slashBigg=47;e.slashbigg=47;e.summationdisplay=8721;e.summationtext=8721;e.tildewide=732;e.tildewider=732;e.tildewidest=732;e.uniondisplay=8899;e.unionmultidisplay=8846;e.unionmultitext=8846;e.unionsqdisplay=8852;e.unionsqtext=8852;e.uniontext=8899;e.vextenddouble=8741;e.vextendsingle=8739})),Hi=getLookupTableFactory((function(e){e.space=32;e.a1=9985;e.a2=9986;e.a202=9987;e.a3=9988;e.a4=9742;e.a5=9990;e.a119=9991;e.a118=9992;e.a117=9993;e.a11=9755;e.a12=9758;e.a13=9996;e.a14=9997;e.a15=9998;e.a16=9999;e.a105=1e4;e.a17=10001;e.a18=10002;e.a19=10003;e.a20=10004;e.a21=10005;e.a22=10006;e.a23=10007;e.a24=10008;e.a25=10009;e.a26=10010;e.a27=10011;e.a28=10012;e.a6=10013;e.a7=10014;e.a8=10015;e.a9=10016;e.a10=10017;e.a29=10018;e.a30=10019;e.a31=10020;e.a32=10021;e.a33=10022;e.a34=10023;e.a35=9733;e.a36=10025;e.a37=10026;e.a38=10027;e.a39=10028;e.a40=10029;e.a41=10030;e.a42=10031;e.a43=10032;e.a44=10033;e.a45=10034;e.a46=10035;e.a47=10036;e.a48=10037;e.a49=10038;e.a50=10039;e.a51=10040;e.a52=10041;e.a53=10042;e.a54=10043;e.a55=10044;e.a56=10045;e.a57=10046;e.a58=10047;e.a59=10048;e.a60=10049;e.a61=10050;e.a62=10051;e.a63=10052;e.a64=10053;e.a65=10054;e.a66=10055;e.a67=10056;e.a68=10057;e.a69=10058;e.a70=10059;e.a71=9679;e.a72=10061;e.a73=9632;e.a74=10063;e.a203=10064;e.a75=10065;e.a204=10066;e.a76=9650;e.a77=9660;e.a78=9670;e.a79=10070;e.a81=9687;e.a82=10072;e.a83=10073;e.a84=10074;e.a97=10075;e.a98=10076;e.a99=10077;e.a100=10078;e.a101=10081;e.a102=10082;e.a103=10083;e.a104=10084;e.a106=10085;e.a107=10086;e.a108=10087;e.a112=9827;e.a111=9830;e.a110=9829;e.a109=9824;e.a120=9312;e.a121=9313;e.a122=9314;e.a123=9315;e.a124=9316;e.a125=9317;e.a126=9318;e.a127=9319;e.a128=9320;e.a129=9321;e.a130=10102;e.a131=10103;e.a132=10104;e.a133=10105;e.a134=10106;e.a135=10107;e.a136=10108;e.a137=10109;e.a138=10110;e.a139=10111;e.a140=10112;e.a141=10113;e.a142=10114;e.a143=10115;e.a144=10116;e.a145=10117;e.a146=10118;e.a147=10119;e.a148=10120;e.a149=10121;e.a150=10122;e.a151=10123;e.a152=10124;e.a153=10125;e.a154=10126;e.a155=10127;e.a156=10128;e.a157=10129;e.a158=10130;e.a159=10131;e.a160=10132;e.a161=8594;e.a163=8596;e.a164=8597;e.a196=10136;e.a165=10137;e.a192=10138;e.a166=10139;e.a167=10140;e.a168=10141;e.a169=10142;e.a170=10143;e.a171=10144;e.a172=10145;e.a173=10146;e.a162=10147;e.a174=10148;e.a175=10149;e.a176=10150;e.a177=10151;e.a178=10152;e.a179=10153;e.a193=10154;e.a180=10155;e.a199=10156;e.a181=10157;e.a200=10158;e.a182=10159;e.a201=10161;e.a183=10162;e.a184=10163;e.a197=10164;e.a185=10165;e.a194=10166;e.a198=10167;e.a186=10168;e.a195=10169;e.a187=10170;e.a188=10171;e.a189=10172;e.a190=10173;e.a191=10174;e.a89=10088;e.a90=10089;e.a93=10090;e.a94=10091;e.a91=10092;e.a92=10093;e.a205=10094;e.a85=10095;e.a206=10096;e.a86=10097;e.a87=10098;e.a88=10099;e.a95=10100;e.a96=10101;e[".notdef"]=0})),Ji=getLookupTableFactory((function(e){e[63721]=169;e[63193]=169;e[63720]=174;e[63194]=174;e[63722]=8482;e[63195]=8482;e[63729]=9127;e[63730]=9128;e[63731]=9129;e[63740]=9131;e[63741]=9132;e[63742]=9133;e[63726]=9121;e[63727]=9122;e[63728]=9123;e[63737]=9124;e[63738]=9125;e[63739]=9126;e[63723]=9115;e[63724]=9116;e[63725]=9117;e[63734]=9118;e[63735]=9119;e[63736]=9120}));function getUnicodeForGlyph(e,t){let i=t[e];if(void 0!==i)return i;if(!e)return-1;if("u"===e[0]){const t=e.length;let a;if(7===t&&"n"===e[1]&&"i"===e[2])a=e.substring(3);else{if(!(t>=5&&t<=7))return-1;a=e.substring(1)}if(a===a.toUpperCase()){i=parseInt(a,16);if(i>=0)return i}}return-1}const Yi=[[0,127],[128,255],[256,383],[384,591],[592,687,7424,7551,7552,7615],[688,767,42752,42783],[768,879,7616,7679],[880,1023],[11392,11519],[1024,1279,1280,1327,11744,11775,42560,42655],[1328,1423],[1424,1535],[42240,42559],[1536,1791,1872,1919],[1984,2047],[2304,2431],[2432,2559],[2560,2687],[2688,2815],[2816,2943],[2944,3071],[3072,3199],[3200,3327],[3328,3455],[3584,3711],[3712,3839],[4256,4351,11520,11567],[6912,7039],[4352,4607],[7680,7935,11360,11391,42784,43007],[7936,8191],[8192,8303,11776,11903],[8304,8351],[8352,8399],[8400,8447],[8448,8527],[8528,8591],[8592,8703,10224,10239,10496,10623,11008,11263],[8704,8959,10752,11007,10176,10223,10624,10751],[8960,9215],[9216,9279],[9280,9311],[9312,9471],[9472,9599],[9600,9631],[9632,9727],[9728,9983],[9984,10175],[12288,12351],[12352,12447],[12448,12543,12784,12799],[12544,12591,12704,12735],[12592,12687],[43072,43135],[12800,13055],[13056,13311],[44032,55215],[55296,57343],[67840,67871],[19968,40959,11904,12031,12032,12255,12272,12287,13312,19903,131072,173791,12688,12703],[57344,63743],[12736,12783,63744,64255,194560,195103],[64256,64335],[64336,65023],[65056,65071],[65040,65055],[65104,65135],[65136,65279],[65280,65519],[65520,65535],[3840,4095],[1792,1871],[1920,1983],[3456,3583],[4096,4255],[4608,4991,4992,5023,11648,11743],[5024,5119],[5120,5759],[5760,5791],[5792,5887],[6016,6143],[6144,6319],[10240,10495],[40960,42127],[5888,5919,5920,5951,5952,5983,5984,6015],[66304,66351],[66352,66383],[66560,66639],[118784,119039,119040,119295,119296,119375],[119808,120831],[1044480,1048573],[65024,65039,917760,917999],[917504,917631],[6400,6479],[6480,6527],[6528,6623],[6656,6687],[11264,11359],[11568,11647],[19904,19967],[43008,43055],[65536,65663,65664,65791,65792,65855],[65856,65935],[66432,66463],[66464,66527],[66640,66687],[66688,66735],[67584,67647],[68096,68191],[119552,119647],[73728,74751,74752,74879],[119648,119679],[7040,7103],[7168,7247],[7248,7295],[43136,43231],[43264,43311],[43312,43359],[43520,43615],[65936,65999],[66e3,66047],[66208,66271,66176,66207,67872,67903],[127024,127135,126976,127023]];function getUnicodeRangeFor(e,t=-1){if(-1!==t){const i=Yi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}for(let t=0,i=Yi.length;t<i;t++){const i=Yi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}return-1}const vi=new RegExp("^(\\s)|(\\p{Mn})|(\\p{Cf})$","u"),Ki=new Map;const Ti=!0,qi=1,Oi=2,Pi=4,Wi=32,ji=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function recoverGlyphName(e,t){if(void 0!==t[e])return e;const i=getUnicodeForGlyph(e,t);if(-1!==i)for(const e in t)if(t[e]===i)return e;info("Unable to recover a standard glyph name for: "+e);return e}function type1FontGlyphMapping(e,t,i){const a=Object.create(null);let s,r,n;const g=!!(e.flags&Pi);if(e.isInternalFont){n=t;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(e.baseEncodingName){n=getEncoding(e.baseEncodingName);for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(g)for(r in t)a[r]=t[r];else{n=yi;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}const o=e.differences;let c;if(o)for(r in o){const e=o[r];s=i.indexOf(e);if(-1===s){c||(c=Mi());const t=recoverGlyphName(e,c);t!==e&&(s=i.indexOf(t))}a[r]=s>=0?s:0}return a}function normalizeFontName(e){return e.replaceAll(/[,_]/g,"-").replaceAll(/\s/g,"")}const Xi=getLookupTableFactory((e=>{e[8211]=65074;e[8212]=65073;e[8229]=65072;e[8230]=65049;e[12289]=65041;e[12290]=65042;e[12296]=65087;e[12297]=65088;e[12298]=65085;e[12299]=65086;e[12300]=65089;e[12301]=65090;e[12302]=65091;e[12303]=65092;e[12304]=65083;e[12305]=65084;e[12308]=65081;e[12309]=65082;e[12310]=65047;e[12311]=65048;e[65103]=65076;e[65281]=65045;e[65288]=65077;e[65289]=65078;e[65292]=65040;e[65306]=65043;e[65307]=65044;e[65311]=65046;e[65339]=65095;e[65341]=65096;e[65343]=65075;e[65371]=65079;e[65373]=65080})),Zi=getLookupTableFactory((function(e){e["Times-Roman"]="Times-Roman";e.Helvetica="Helvetica";e.Courier="Courier";e.Symbol="Symbol";e["Times-Bold"]="Times-Bold";e["Helvetica-Bold"]="Helvetica-Bold";e["Courier-Bold"]="Courier-Bold";e.ZapfDingbats="ZapfDingbats";e["Times-Italic"]="Times-Italic";e["Helvetica-Oblique"]="Helvetica-Oblique";e["Courier-Oblique"]="Courier-Oblique";e["Times-BoldItalic"]="Times-BoldItalic";e["Helvetica-BoldOblique"]="Helvetica-BoldOblique";e["Courier-BoldOblique"]="Courier-BoldOblique";e.ArialNarrow="Helvetica";e["ArialNarrow-Bold"]="Helvetica-Bold";e["ArialNarrow-BoldItalic"]="Helvetica-BoldOblique";e["ArialNarrow-Italic"]="Helvetica-Oblique";e.ArialBlack="Helvetica";e["ArialBlack-Bold"]="Helvetica-Bold";e["ArialBlack-BoldItalic"]="Helvetica-BoldOblique";e["ArialBlack-Italic"]="Helvetica-Oblique";e["Arial-Black"]="Helvetica";e["Arial-Black-Bold"]="Helvetica-Bold";e["Arial-Black-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Black-Italic"]="Helvetica-Oblique";e.Arial="Helvetica";e["Arial-Bold"]="Helvetica-Bold";e["Arial-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Italic"]="Helvetica-Oblique";e.ArialMT="Helvetica";e["Arial-BoldItalicMT"]="Helvetica-BoldOblique";e["Arial-BoldMT"]="Helvetica-Bold";e["Arial-ItalicMT"]="Helvetica-Oblique";e["Arial-BoldItalicMT-BoldItalic"]="Helvetica-BoldOblique";e["Arial-BoldMT-Bold"]="Helvetica-Bold";e["Arial-ItalicMT-Italic"]="Helvetica-Oblique";e.ArialUnicodeMS="Helvetica";e["ArialUnicodeMS-Bold"]="Helvetica-Bold";e["ArialUnicodeMS-BoldItalic"]="Helvetica-BoldOblique";e["ArialUnicodeMS-Italic"]="Helvetica-Oblique";e["Courier-BoldItalic"]="Courier-BoldOblique";e["Courier-Italic"]="Courier-Oblique";e.CourierNew="Courier";e["CourierNew-Bold"]="Courier-Bold";e["CourierNew-BoldItalic"]="Courier-BoldOblique";e["CourierNew-Italic"]="Courier-Oblique";e["CourierNewPS-BoldItalicMT"]="Courier-BoldOblique";e["CourierNewPS-BoldMT"]="Courier-Bold";e["CourierNewPS-ItalicMT"]="Courier-Oblique";e.CourierNewPSMT="Courier";e["Helvetica-BoldItalic"]="Helvetica-BoldOblique";e["Helvetica-Italic"]="Helvetica-Oblique";e["Symbol-Bold"]="Symbol";e["Symbol-BoldItalic"]="Symbol";e["Symbol-Italic"]="Symbol";e.TimesNewRoman="Times-Roman";e["TimesNewRoman-Bold"]="Times-Bold";e["TimesNewRoman-BoldItalic"]="Times-BoldItalic";e["TimesNewRoman-Italic"]="Times-Italic";e.TimesNewRomanPS="Times-Roman";e["TimesNewRomanPS-Bold"]="Times-Bold";e["TimesNewRomanPS-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPS-BoldItalicMT"]="Times-BoldItalic";e["TimesNewRomanPS-BoldMT"]="Times-Bold";e["TimesNewRomanPS-Italic"]="Times-Italic";e["TimesNewRomanPS-ItalicMT"]="Times-Italic";e.TimesNewRomanPSMT="Times-Roman";e["TimesNewRomanPSMT-Bold"]="Times-Bold";e["TimesNewRomanPSMT-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPSMT-Italic"]="Times-Italic"})),Vi=getLookupTableFactory((function(e){e.Courier="FoxitFixed.pfb";e["Courier-Bold"]="FoxitFixedBold.pfb";e["Courier-BoldOblique"]="FoxitFixedBoldItalic.pfb";e["Courier-Oblique"]="FoxitFixedItalic.pfb";e.Helvetica="LiberationSans-Regular.ttf";e["Helvetica-Bold"]="LiberationSans-Bold.ttf";e["Helvetica-BoldOblique"]="LiberationSans-BoldItalic.ttf";e["Helvetica-Oblique"]="LiberationSans-Italic.ttf";e["Times-Roman"]="FoxitSerif.pfb";e["Times-Bold"]="FoxitSerifBold.pfb";e["Times-BoldItalic"]="FoxitSerifBoldItalic.pfb";e["Times-Italic"]="FoxitSerifItalic.pfb";e.Symbol="FoxitSymbol.pfb";e.ZapfDingbats="FoxitDingbats.pfb";e["LiberationSans-Regular"]="LiberationSans-Regular.ttf";e["LiberationSans-Bold"]="LiberationSans-Bold.ttf";e["LiberationSans-Italic"]="LiberationSans-Italic.ttf";e["LiberationSans-BoldItalic"]="LiberationSans-BoldItalic.ttf"})),zi=getLookupTableFactory((function(e){e.Calibri="Helvetica";e["Calibri-Bold"]="Helvetica-Bold";e["Calibri-BoldItalic"]="Helvetica-BoldOblique";e["Calibri-Italic"]="Helvetica-Oblique";e.CenturyGothic="Helvetica";e["CenturyGothic-Bold"]="Helvetica-Bold";e["CenturyGothic-BoldItalic"]="Helvetica-BoldOblique";e["CenturyGothic-Italic"]="Helvetica-Oblique";e.ComicSansMS="Comic Sans MS";e["ComicSansMS-Bold"]="Comic Sans MS-Bold";e["ComicSansMS-BoldItalic"]="Comic Sans MS-BoldItalic";e["ComicSansMS-Italic"]="Comic Sans MS-Italic";e.GillSansMT="Helvetica";e["GillSansMT-Bold"]="Helvetica-Bold";e["GillSansMT-BoldItalic"]="Helvetica-BoldOblique";e["GillSansMT-Italic"]="Helvetica-Oblique";e.Impact="Helvetica";e["ItcSymbol-Bold"]="Helvetica-Bold";e["ItcSymbol-BoldItalic"]="Helvetica-BoldOblique";e["ItcSymbol-Book"]="Helvetica";e["ItcSymbol-BookItalic"]="Helvetica-Oblique";e["ItcSymbol-Medium"]="Helvetica";e["ItcSymbol-MediumItalic"]="Helvetica-Oblique";e.LucidaConsole="Courier";e["LucidaConsole-Bold"]="Courier-Bold";e["LucidaConsole-BoldItalic"]="Courier-BoldOblique";e["LucidaConsole-Italic"]="Courier-Oblique";e["LucidaSans-Demi"]="Helvetica-Bold";e["MS-Gothic"]="MS Gothic";e["MS-Gothic-Bold"]="MS Gothic-Bold";e["MS-Gothic-BoldItalic"]="MS Gothic-BoldItalic";e["MS-Gothic-Italic"]="MS Gothic-Italic";e["MS-Mincho"]="MS Mincho";e["MS-Mincho-Bold"]="MS Mincho-Bold";e["MS-Mincho-BoldItalic"]="MS Mincho-BoldItalic";e["MS-Mincho-Italic"]="MS Mincho-Italic";e["MS-PGothic"]="MS PGothic";e["MS-PGothic-Bold"]="MS PGothic-Bold";e["MS-PGothic-BoldItalic"]="MS PGothic-BoldItalic";e["MS-PGothic-Italic"]="MS PGothic-Italic";e["MS-PMincho"]="MS PMincho";e["MS-PMincho-Bold"]="MS PMincho-Bold";e["MS-PMincho-BoldItalic"]="MS PMincho-BoldItalic";e["MS-PMincho-Italic"]="MS PMincho-Italic";e.NuptialScript="Times-Italic";e.SegoeUISymbol="Helvetica"})),_i=getLookupTableFactory((function(e){e["Adobe Jenson"]=!0;e["Adobe Text"]=!0;e.Albertus=!0;e.Aldus=!0;e.Alexandria=!0;e.Algerian=!0;e["American Typewriter"]=!0;e.Antiqua=!0;e.Apex=!0;e.Arno=!0;e.Aster=!0;e.Aurora=!0;e.Baskerville=!0;e.Bell=!0;e.Bembo=!0;e["Bembo Schoolbook"]=!0;e.Benguiat=!0;e["Berkeley Old Style"]=!0;e["Bernhard Modern"]=!0;e["Berthold City"]=!0;e.Bodoni=!0;e["Bauer Bodoni"]=!0;e["Book Antiqua"]=!0;e.Bookman=!0;e["Bordeaux Roman"]=!0;e["Californian FB"]=!0;e.Calisto=!0;e.Calvert=!0;e.Capitals=!0;e.Cambria=!0;e.Cartier=!0;e.Caslon=!0;e.Catull=!0;e.Centaur=!0;e["Century Old Style"]=!0;e["Century Schoolbook"]=!0;e.Chaparral=!0;e["Charis SIL"]=!0;e.Cheltenham=!0;e["Cholla Slab"]=!0;e.Clarendon=!0;e.Clearface=!0;e.Cochin=!0;e.Colonna=!0;e["Computer Modern"]=!0;e["Concrete Roman"]=!0;e.Constantia=!0;e["Cooper Black"]=!0;e.Corona=!0;e.Ecotype=!0;e.Egyptienne=!0;e.Elephant=!0;e.Excelsior=!0;e.Fairfield=!0;e["FF Scala"]=!0;e.Folkard=!0;e.Footlight=!0;e.FreeSerif=!0;e["Friz Quadrata"]=!0;e.Garamond=!0;e.Gentium=!0;e.Georgia=!0;e.Gloucester=!0;e["Goudy Old Style"]=!0;e["Goudy Schoolbook"]=!0;e["Goudy Pro Font"]=!0;e.Granjon=!0;e["Guardian Egyptian"]=!0;e.Heather=!0;e.Hercules=!0;e["High Tower Text"]=!0;e.Hiroshige=!0;e["Hoefler Text"]=!0;e["Humana Serif"]=!0;e.Imprint=!0;e["Ionic No. 5"]=!0;e.Janson=!0;e.Joanna=!0;e.Korinna=!0;e.Lexicon=!0;e.LiberationSerif=!0;e["Liberation Serif"]=!0;e["Linux Libertine"]=!0;e.Literaturnaya=!0;e.Lucida=!0;e["Lucida Bright"]=!0;e.Melior=!0;e.Memphis=!0;e.Miller=!0;e.Minion=!0;e.Modern=!0;e["Mona Lisa"]=!0;e["Mrs Eaves"]=!0;e["MS Serif"]=!0;e["Museo Slab"]=!0;e["New York"]=!0;e["Nimbus Roman"]=!0;e["NPS Rawlinson Roadway"]=!0;e.NuptialScript=!0;e.Palatino=!0;e.Perpetua=!0;e.Plantin=!0;e["Plantin Schoolbook"]=!0;e.Playbill=!0;e["Poor Richard"]=!0;e["Rawlinson Roadway"]=!0;e.Renault=!0;e.Requiem=!0;e.Rockwell=!0;e.Roman=!0;e["Rotis Serif"]=!0;e.Sabon=!0;e.Scala=!0;e.Seagull=!0;e.Sistina=!0;e.Souvenir=!0;e.STIX=!0;e["Stone Informal"]=!0;e["Stone Serif"]=!0;e.Sylfaen=!0;e.Times=!0;e.Trajan=!0;e["Trinité"]=!0;e["Trump Mediaeval"]=!0;e.Utopia=!0;e["Vale Type"]=!0;e["Bitstream Vera"]=!0;e["Vera Serif"]=!0;e.Versailles=!0;e.Wanted=!0;e.Weiss=!0;e["Wide Latin"]=!0;e.Windsor=!0;e.XITS=!0})),$i=getLookupTableFactory((function(e){e.Dingbats=!0;e.Symbol=!0;e.ZapfDingbats=!0;e.Wingdings=!0;e["Wingdings-Bold"]=!0;e["Wingdings-Regular"]=!0})),Aa=getLookupTableFactory((function(e){e[2]=10;e[3]=32;e[4]=33;e[5]=34;e[6]=35;e[7]=36;e[8]=37;e[9]=38;e[10]=39;e[11]=40;e[12]=41;e[13]=42;e[14]=43;e[15]=44;e[16]=45;e[17]=46;e[18]=47;e[19]=48;e[20]=49;e[21]=50;e[22]=51;e[23]=52;e[24]=53;e[25]=54;e[26]=55;e[27]=56;e[28]=57;e[29]=58;e[30]=894;e[31]=60;e[32]=61;e[33]=62;e[34]=63;e[35]=64;e[36]=65;e[37]=66;e[38]=67;e[39]=68;e[40]=69;e[41]=70;e[42]=71;e[43]=72;e[44]=73;e[45]=74;e[46]=75;e[47]=76;e[48]=77;e[49]=78;e[50]=79;e[51]=80;e[52]=81;e[53]=82;e[54]=83;e[55]=84;e[56]=85;e[57]=86;e[58]=87;e[59]=88;e[60]=89;e[61]=90;e[62]=91;e[63]=92;e[64]=93;e[65]=94;e[66]=95;e[67]=96;e[68]=97;e[69]=98;e[70]=99;e[71]=100;e[72]=101;e[73]=102;e[74]=103;e[75]=104;e[76]=105;e[77]=106;e[78]=107;e[79]=108;e[80]=109;e[81]=110;e[82]=111;e[83]=112;e[84]=113;e[85]=114;e[86]=115;e[87]=116;e[88]=117;e[89]=118;e[90]=119;e[91]=120;e[92]=121;e[93]=122;e[94]=123;e[95]=124;e[96]=125;e[97]=126;e[98]=196;e[99]=197;e[100]=199;e[101]=201;e[102]=209;e[103]=214;e[104]=220;e[105]=225;e[106]=224;e[107]=226;e[108]=228;e[109]=227;e[110]=229;e[111]=231;e[112]=233;e[113]=232;e[114]=234;e[115]=235;e[116]=237;e[117]=236;e[118]=238;e[119]=239;e[120]=241;e[121]=243;e[122]=242;e[123]=244;e[124]=246;e[125]=245;e[126]=250;e[127]=249;e[128]=251;e[129]=252;e[130]=8224;e[131]=176;e[132]=162;e[133]=163;e[134]=167;e[135]=8226;e[136]=182;e[137]=223;e[138]=174;e[139]=169;e[140]=8482;e[141]=180;e[142]=168;e[143]=8800;e[144]=198;e[145]=216;e[146]=8734;e[147]=177;e[148]=8804;e[149]=8805;e[150]=165;e[151]=181;e[152]=8706;e[153]=8721;e[154]=8719;e[156]=8747;e[157]=170;e[158]=186;e[159]=8486;e[160]=230;e[161]=248;e[162]=191;e[163]=161;e[164]=172;e[165]=8730;e[166]=402;e[167]=8776;e[168]=8710;e[169]=171;e[170]=187;e[171]=8230;e[179]=8220;e[180]=8221;e[181]=8216;e[182]=8217;e[200]=193;e[203]=205;e[207]=211;e[210]=218;e[223]=711;e[224]=321;e[225]=322;e[226]=352;e[227]=353;e[228]=381;e[229]=382;e[233]=221;e[234]=253;e[252]=263;e[253]=268;e[254]=269;e[258]=258;e[260]=260;e[261]=261;e[265]=280;e[266]=281;e[267]=282;e[268]=283;e[269]=313;e[275]=323;e[276]=324;e[278]=328;e[283]=344;e[284]=345;e[285]=346;e[286]=347;e[292]=367;e[295]=377;e[296]=378;e[298]=380;e[305]=963;e[306]=964;e[307]=966;e[308]=8215;e[309]=8252;e[310]=8319;e[311]=8359;e[312]=8592;e[313]=8593;e[337]=9552;e[493]=1039;e[494]=1040;e[672]=1488;e[673]=1489;e[674]=1490;e[675]=1491;e[676]=1492;e[677]=1493;e[678]=1494;e[679]=1495;e[680]=1496;e[681]=1497;e[682]=1498;e[683]=1499;e[684]=1500;e[685]=1501;e[686]=1502;e[687]=1503;e[688]=1504;e[689]=1505;e[690]=1506;e[691]=1507;e[692]=1508;e[693]=1509;e[694]=1510;e[695]=1511;e[696]=1512;e[697]=1513;e[698]=1514;e[705]=1524;e[706]=8362;e[710]=64288;e[711]=64298;e[759]=1617;e[761]=1776;e[763]=1778;e[775]=1652;e[777]=1764;e[778]=1780;e[779]=1781;e[780]=1782;e[782]=771;e[783]=64726;e[786]=8363;e[788]=8532;e[790]=768;e[791]=769;e[792]=768;e[795]=803;e[797]=64336;e[798]=64337;e[799]=64342;e[800]=64343;e[801]=64344;e[802]=64345;e[803]=64362;e[804]=64363;e[805]=64364;e[2424]=7821;e[2425]=7822;e[2426]=7823;e[2427]=7824;e[2428]=7825;e[2429]=7826;e[2430]=7827;e[2433]=7682;e[2678]=8045;e[2679]=8046;e[2830]=1552;e[2838]=686;e[2840]=751;e[2842]=753;e[2843]=754;e[2844]=755;e[2846]=757;e[2856]=767;e[2857]=848;e[2858]=849;e[2862]=853;e[2863]=854;e[2864]=855;e[2865]=861;e[2866]=862;e[2906]=7460;e[2908]=7462;e[2909]=7463;e[2910]=7464;e[2912]=7466;e[2913]=7467;e[2914]=7468;e[2916]=7470;e[2917]=7471;e[2918]=7472;e[2920]=7474;e[2921]=7475;e[2922]=7476;e[2924]=7478;e[2925]=7479;e[2926]=7480;e[2928]=7482;e[2929]=7483;e[2930]=7484;e[2932]=7486;e[2933]=7487;e[2934]=7488;e[2936]=7490;e[2937]=7491;e[2938]=7492;e[2940]=7494;e[2941]=7495;e[2942]=7496;e[2944]=7498;e[2946]=7500;e[2948]=7502;e[2950]=7504;e[2951]=7505;e[2952]=7506;e[2954]=7508;e[2955]=7509;e[2956]=7510;e[2958]=7512;e[2959]=7513;e[2960]=7514;e[2962]=7516;e[2963]=7517;e[2964]=7518;e[2966]=7520;e[2967]=7521;e[2968]=7522;e[2970]=7524;e[2971]=7525;e[2972]=7526;e[2974]=7528;e[2975]=7529;e[2976]=7530;e[2978]=1537;e[2979]=1538;e[2980]=1539;e[2982]=1549;e[2983]=1551;e[2984]=1552;e[2986]=1554;e[2987]=1555;e[2988]=1556;e[2990]=1623;e[2991]=1624;e[2995]=1775;e[2999]=1791;e[3002]=64290;e[3003]=64291;e[3004]=64292;e[3006]=64294;e[3007]=64295;e[3008]=64296;e[3011]=1900;e[3014]=8223;e[3015]=8244;e[3017]=7532;e[3018]=7533;e[3019]=7534;e[3075]=7590;e[3076]=7591;e[3079]=7594;e[3080]=7595;e[3083]=7598;e[3084]=7599;e[3087]=7602;e[3088]=7603;e[3091]=7606;e[3092]=7607;e[3095]=7610;e[3096]=7611;e[3099]=7614;e[3100]=7615;e[3103]=7618;e[3104]=7619;e[3107]=8337;e[3108]=8338;e[3116]=1884;e[3119]=1885;e[3120]=1885;e[3123]=1886;e[3124]=1886;e[3127]=1887;e[3128]=1887;e[3131]=1888;e[3132]=1888;e[3135]=1889;e[3136]=1889;e[3139]=1890;e[3140]=1890;e[3143]=1891;e[3144]=1891;e[3147]=1892;e[3148]=1892;e[3153]=580;e[3154]=581;e[3157]=584;e[3158]=585;e[3161]=588;e[3162]=589;e[3165]=891;e[3166]=892;e[3169]=1274;e[3170]=1275;e[3173]=1278;e[3174]=1279;e[3181]=7622;e[3182]=7623;e[3282]=11799;e[3316]=578;e[3379]=42785;e[3393]=1159;e[3416]=8377})),ea=getLookupTableFactory((function(e){e[227]=322;e[264]=261;e[291]=346})),ta=getLookupTableFactory((function(e){e[1]=32;e[4]=65;e[5]=192;e[6]=193;e[9]=196;e[17]=66;e[18]=67;e[21]=268;e[24]=68;e[28]=69;e[29]=200;e[30]=201;e[32]=282;e[38]=70;e[39]=71;e[44]=72;e[47]=73;e[48]=204;e[49]=205;e[58]=74;e[60]=75;e[62]=76;e[68]=77;e[69]=78;e[75]=79;e[76]=210;e[80]=214;e[87]=80;e[89]=81;e[90]=82;e[92]=344;e[94]=83;e[97]=352;e[100]=84;e[104]=85;e[109]=220;e[115]=86;e[116]=87;e[121]=88;e[122]=89;e[124]=221;e[127]=90;e[129]=381;e[258]=97;e[259]=224;e[260]=225;e[263]=228;e[268]=261;e[271]=98;e[272]=99;e[273]=263;e[275]=269;e[282]=100;e[286]=101;e[287]=232;e[288]=233;e[290]=283;e[295]=281;e[296]=102;e[336]=103;e[346]=104;e[349]=105;e[350]=236;e[351]=237;e[361]=106;e[364]=107;e[367]=108;e[371]=322;e[373]=109;e[374]=110;e[381]=111;e[382]=242;e[383]=243;e[386]=246;e[393]=112;e[395]=113;e[396]=114;e[398]=345;e[400]=115;e[401]=347;e[403]=353;e[410]=116;e[437]=117;e[442]=252;e[448]=118;e[449]=119;e[454]=120;e[455]=121;e[457]=253;e[460]=122;e[462]=382;e[463]=380;e[853]=44;e[855]=58;e[856]=46;e[876]=47;e[878]=45;e[882]=45;e[894]=40;e[895]=41;e[896]=91;e[897]=93;e[923]=64;e[1004]=48;e[1005]=49;e[1006]=50;e[1007]=51;e[1008]=52;e[1009]=53;e[1010]=54;e[1011]=55;e[1012]=56;e[1013]=57;e[1081]=37;e[1085]=43;e[1086]=45}));function getStandardFontName(e){const t=normalizeFontName(e);return Zi()[t]}function isKnownFontName(e){const t=normalizeFontName(e);return!!(Zi()[t]||zi()[t]||_i()[t]||$i()[t])}class ToUnicodeMap{constructor(e=[]){this._map=e}get length(){return this._map.length}forEach(e){for(const t in this._map)e(t,this._map[t].charCodeAt(0))}has(e){return void 0!==this._map[e]}get(e){return this._map[e]}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}amend(e){for(const t in e)this._map[t]=e[t]}}class IdentityToUnicodeMap{constructor(e,t){this.firstChar=e;this.lastChar=t}get length(){return this.lastChar+1-this.firstChar}forEach(e){for(let t=this.firstChar,i=this.lastChar;t<=i;t++)e(t,t)}has(e){return this.firstChar<=e&&e<=this.lastChar}get(e){if(this.firstChar<=e&&e<=this.lastChar)return String.fromCharCode(e)}charCodeOf(e){return Number.isInteger(e)&&e>=this.firstChar&&e<=this.lastChar?e:-1}amend(e){unreachable("Should not call amend()")}}class CFFFont{constructor(e,t){this.properties=t;const i=new CFFParser(e,t,Ti);this.cff=i.parse();this.cff.duplicateFirstGlyph();const a=new CFFCompiler(this.cff);this.seacs=this.cff.seacs;try{this.data=a.compile()}catch{warn("Failed to compile font "+t.loadedName);this.data=e}this._createBuiltInEncoding()}get numGlyphs(){return this.cff.charStrings.count}getCharset(){return this.cff.charset.charset}getGlyphMapping(){const e=this.cff,t=this.properties,{cidToGidMap:i,cMap:a}=t,s=e.charset.charset;let r,n;if(t.composite){let t,g;if(i?.length>0){t=Object.create(null);for(let e=0,a=i.length;e<a;e++){const a=i[e];void 0!==a&&(t[a]=e)}}r=Object.create(null);if(e.isCIDFont)for(n=0;n<s.length;n++){const e=s[n];g=a.charCodeOf(e);void 0!==t?.[g]&&(g=t[g]);r[g]=n}else for(n=0;n<e.charStrings.count;n++){g=a.charCodeOf(n);r[g]=n}return r}let g=e.encoding?e.encoding.encoding:null;t.isInternalFont&&(g=t.defaultEncoding);r=type1FontGlyphMapping(t,g,s);return r}hasGlyphId(e){return this.cff.hasGlyphId(e)}_createBuiltInEncoding(){const{charset:e,encoding:t}=this.cff;if(!e||!t)return;const i=e.charset,a=t.encoding,s=[];for(const e in a){const t=a[e];if(t>=0){const a=i[t];a&&(s[e]=a)}}s.length>0&&(this.properties.builtInEncoding=s)}}function getUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function getUint16(e,t){return e[t]<<8|e[t+1]}function getInt16(e,t){return(e[t]<<24|e[t+1]<<16)>>16}function getInt8(e,t){return e[t]<<24>>24}function getFloat214(e,t){return getInt16(e,t)/16384}function getSubroutineBias(e){const t=e.length;let i=32768;t<1240?i=107:t<33900&&(i=1131);return i}function parseCmap(e,t,i){const a=1===getUint16(e,t+2)?getUint32(e,t+8):getUint32(e,t+16),s=getUint16(e,t+a);let r,n,g;if(4===s){getUint16(e,t+a+2);const i=getUint16(e,t+a+6)>>1;n=t+a+14;r=[];for(g=0;g<i;g++,n+=2)r[g]={end:getUint16(e,n)};n+=2;for(g=0;g<i;g++,n+=2)r[g].start=getUint16(e,n);for(g=0;g<i;g++,n+=2)r[g].idDelta=getUint16(e,n);for(g=0;g<i;g++,n+=2){let t=getUint16(e,n);if(0!==t){r[g].ids=[];for(let i=0,a=r[g].end-r[g].start+1;i<a;i++){r[g].ids[i]=getUint16(e,n+t);t+=2}}}return r}if(12===s){const i=getUint32(e,t+a+12);n=t+a+16;r=[];for(g=0;g<i;g++){t=getUint32(e,n);r.push({start:t,end:getUint32(e,n+4),idDelta:getUint32(e,n+8)-t});n+=12}return r}throw new FormatError(`unsupported cmap: ${s}`)}function parseCff(e,t,i,a){const s=new CFFParser(new Stream(e,t,i-t),{},a).parse();return{glyphs:s.charStrings.objects,subrs:s.topDict.privateDict?.subrsIndex?.objects,gsubrs:s.globalSubrIndex?.objects,isCFFCIDFont:s.isCIDFont,fdSelect:s.fdSelect,fdArray:s.fdArray}}function lookupCmap(e,t){const i=t.codePointAt(0);let a=0,s=0,r=e.length-1;for(;s<r;){const t=s+r+1>>1;i<e[t].start?r=t-1:s=t}e[s].start<=i&&i<=e[s].end&&(a=e[s].idDelta+(e[s].ids?e[s].ids[i-e[s].start]:i)&65535);return{charCode:i,glyphId:a}}function compileGlyf(e,t,i){function moveTo(e,i){t.add(lt,[e,i])}function lineTo(e,i){t.add(Qt,[e,i])}function quadraticCurveTo(e,i,a,s){t.add(Et,[e,i,a,s])}let a=0;const s=getInt16(e,a);let r,n=0,g=0;a+=10;if(s<0)do{r=getUint16(e,a);const s=getUint16(e,a+2);a+=4;let o,c;if(1&r){if(2&r){o=getInt16(e,a);c=getInt16(e,a+2)}else{o=getUint16(e,a);c=getUint16(e,a+2)}a+=4}else if(2&r){o=getInt8(e,a++);c=getInt8(e,a++)}else{o=e[a++];c=e[a++]}if(2&r){n=o;g=c}else{n=0;g=0}let C=1,h=1,l=0,Q=0;if(8&r){C=h=getFloat214(e,a);a+=2}else if(64&r){C=getFloat214(e,a);h=getFloat214(e,a+2);a+=4}else if(128&r){C=getFloat214(e,a);l=getFloat214(e,a+2);Q=getFloat214(e,a+4);h=getFloat214(e,a+6);a+=8}const E=i.glyphs[s];if(E){t.add(dt);t.add(pt,[C,l,Q,h,n,g]);compileGlyf(E,t,i);t.add(ut)}}while(32&r);else{const t=[];let i,o;for(i=0;i<s;i++){t.push(getUint16(e,a));a+=2}a+=2+getUint16(e,a);const c=t.at(-1)+1,C=[];for(;C.length<c;){r=e[a++];let t=1;8&r&&(t+=e[a++]);for(;t-- >0;)C.push({flags:r})}for(i=0;i<c;i++){switch(18&C[i].flags){case 0:n+=getInt16(e,a);a+=2;break;case 2:n-=e[a++];break;case 18:n+=e[a++]}C[i].x=n}for(i=0;i<c;i++){switch(36&C[i].flags){case 0:g+=getInt16(e,a);a+=2;break;case 4:g-=e[a++];break;case 36:g+=e[a++]}C[i].y=g}let h=0;for(a=0;a<s;a++){const e=t[a],s=C.slice(h,e+1);if(1&s[0].flags)s.push(s[0]);else if(1&s.at(-1).flags)s.unshift(s.at(-1));else{const e={flags:1,x:(s[0].x+s.at(-1).x)/2,y:(s[0].y+s.at(-1).y)/2};s.unshift(e);s.push(e)}moveTo(s[0].x,s[0].y);for(i=1,o=s.length;i<o;i++)if(1&s[i].flags)lineTo(s[i].x,s[i].y);else if(1&s[i+1].flags){quadraticCurveTo(s[i].x,s[i].y,s[i+1].x,s[i+1].y);i++}else quadraticCurveTo(s[i].x,s[i].y,(s[i].x+s[i+1].x)/2,(s[i].y+s[i+1].y)/2);h=e+1}}}function compileCharString(e,t,i,a){function moveTo(e,i){t.add(lt,[e,i])}function lineTo(e,i){t.add(Qt,[e,i])}function bezierCurveTo(e,i,a,s,r,n){t.add(Bt,[e,i,a,s,r,n])}const s=[];let r=0,n=0,g=0;!function parse(e){let o=0;for(;o<e.length;){let c,C,h,l,Q,E,u,d,f,p=!1,m=e[o++];switch(m){case 1:case 3:case 18:case 23:g+=s.length>>1;p=!0;break;case 4:n+=s.pop();moveTo(r,n);p=!0;break;case 5:for(;s.length>0;){r+=s.shift();n+=s.shift();lineTo(r,n)}break;case 6:for(;s.length>0;){r+=s.shift();lineTo(r,n);if(0===s.length)break;n+=s.shift();lineTo(r,n)}break;case 7:for(;s.length>0;){n+=s.shift();lineTo(r,n);if(0===s.length)break;r+=s.shift();lineTo(r,n)}break;case 8:for(;s.length>0;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 10:d=s.pop();f=null;if(i.isCFFCIDFont){const e=i.fdSelect.getFDIndex(a);if(e>=0&&e<i.fdArray.length){const t=i.fdArray[e];let a;t.privateDict?.subrsIndex&&(a=t.privateDict.subrsIndex.objects);if(a){d+=getSubroutineBias(a);f=a[d]}}else warn("Invalid fd index for glyph index.")}else f=i.subrs[d+i.subrsBias];f&&parse(f);break;case 11:return;case 12:m=e[o++];switch(m){case 34:c=r+s.shift();C=c+s.shift();Q=n+s.shift();r=C+s.shift();bezierCurveTo(c,n,C,Q,r,Q);c=r+s.shift();C=c+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,n,r,n);break;case 35:c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);s.pop();break;case 36:c=r+s.shift();Q=n+s.shift();C=c+s.shift();E=Q+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,E,r,E);c=r+s.shift();C=c+s.shift();u=E+s.shift();r=C+s.shift();bezierCurveTo(c,E,C,u,r,n);break;case 37:const e=r,t=n;c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l;Math.abs(r-e)>Math.abs(n-t)?r+=s.shift():n+=s.shift();bezierCurveTo(c,h,C,l,r,n);break;default:throw new FormatError(`unknown operator: 12 ${m}`)}break;case 14:if(s.length>=4){const e=s.pop(),a=s.pop();n=s.pop();r=s.pop();t.add(dt);t.add(mt,[r,n]);let g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[yi[e]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId);t.add(ut);g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[yi[a]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId)}return;case 19:case 20:g+=s.length>>1;o+=g+7>>3;p=!0;break;case 21:n+=s.pop();r+=s.pop();moveTo(r,n);p=!0;break;case 22:r+=s.pop();moveTo(r,n);p=!0;break;case 24:for(;s.length>2;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}r+=s.shift();n+=s.shift();lineTo(r,n);break;case 25:for(;s.length>6;){r+=s.shift();n+=s.shift();lineTo(r,n)}c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);break;case 26:s.length%2&&(r+=s.shift());for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 27:s.length%2&&(n+=s.shift());for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l;bezierCurveTo(c,h,C,l,r,n)}break;case 28:s.push((e[o]<<24|e[o+1]<<16)>>16);o+=2;break;case 29:d=s.pop()+i.gsubrsBias;f=i.gsubrs[d];f&&parse(f);break;case 30:for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;case 31:for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;default:if(m<32)throw new FormatError(`unknown operator: ${m}`);if(m<247)s.push(m-139);else if(m<251)s.push(256*(m-247)+e[o++]+108);else if(m<255)s.push(256*-(m-251)-e[o++]-108);else{s.push((e[o]<<24|e[o+1]<<16|e[o+2]<<8|e[o+3])/65536);o+=4}}p&&(s.length=0)}}(e)}const ia=[];class Commands{cmds=[];add(e,t){if(t)if(isNumberArray(t,null))this.cmds.push(e,...t);else{warn(`Commands.add - "${e}" has at least one non-number arg: "${t}".`);const i=t.map((e=>"number"==typeof e?e:0));this.cmds.push(e,...i)}else this.cmds.push(e)}}class CompiledFont{constructor(e){this.fontMatrix=e;this.compiledGlyphs=Object.create(null);this.compiledCharCodeToGlyphId=Object.create(null)}getPathJs(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);let a,s=this.compiledGlyphs[i];if(!s){try{s=this.compileGlyph(this.glyphs[i],i)}catch(e){s=ia;a=e}this.compiledGlyphs[i]=s}this.compiledCharCodeToGlyphId[t]??=i;if(a)throw a;return s}compileGlyph(e,t){if(!e||0===e.length||14===e[0])return ia;let i=this.fontMatrix;if(this.isCFFCIDFont){const e=this.fdSelect.getFDIndex(t);if(e>=0&&e<this.fdArray.length){i=this.fdArray[e].getByName("FontMatrix")||a}else warn("Invalid fd index for glyph index.")}const s=new Commands;s.add(dt);s.add(pt,i.slice());s.add(ft);this.compileGlyphImpl(e,s,t);s.add(ut);return s.cmds}compileGlyphImpl(){unreachable("Children classes should implement this.")}hasBuiltPath(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);return void 0!==this.compiledGlyphs[i]&&void 0!==this.compiledCharCodeToGlyphId[t]}}class TrueTypeCompiled extends CompiledFont{constructor(e,t,i){super(i||[488e-6,0,0,488e-6,0,0]);this.glyphs=e;this.cmap=t}compileGlyphImpl(e,t){compileGlyf(e,t,this)}}class Type2Compiled extends CompiledFont{constructor(e,t,i,a){super(i||[.001,0,0,.001,0,0]);this.glyphs=e.glyphs;this.gsubrs=e.gsubrs||[];this.subrs=e.subrs||[];this.cmap=t;this.glyphNameMap=a||Mi();this.gsubrsBias=getSubroutineBias(this.gsubrs);this.subrsBias=getSubroutineBias(this.subrs);this.isCFFCIDFont=e.isCFFCIDFont;this.fdSelect=e.fdSelect;this.fdArray=e.fdArray}compileGlyphImpl(e,t,i){compileCharString(e,t,this,i)}}class FontRendererFactory{static create(e,t){const i=new Uint8Array(e.data);let a,s,r,n,g,o;const c=getUint16(i,4);for(let e=0,C=12;e<c;e++,C+=16){const e=bytesToString(i.subarray(C,C+4)),c=getUint32(i,C+8),h=getUint32(i,C+12);switch(e){case"cmap":a=parseCmap(i,c);break;case"glyf":s=i.subarray(c,c+h);break;case"loca":r=i.subarray(c,c+h);break;case"head":o=getUint16(i,c+18);g=getUint16(i,c+50);break;case"CFF ":n=parseCff(i,c,c+h,t)}}if(s){const t=o?[1/o,0,0,1/o,0,0]:e.fontMatrix;return new TrueTypeCompiled(function parseGlyfTable(e,t,i){let a,s;if(i){a=4;s=getUint32}else{a=2;s=(e,t)=>2*getUint16(e,t)}const r=[];let n=s(t,0);for(let i=a;i<t.length;i+=a){const a=s(t,i);r.push(e.subarray(n,a));n=a}return r}(s,r,g),a,t)}return new Type2Compiled(n,a,e.fontMatrix,e.glyphNameMap)}}const aa=getLookupTableFactory((function(e){e.Courier=600;e["Courier-Bold"]=600;e["Courier-BoldOblique"]=600;e["Courier-Oblique"]=600;e.Helvetica=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Bold"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-BoldOblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Oblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e.Symbol=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.universal=713;e.numbersign=500;e.existential=549;e.percent=833;e.ampersand=778;e.suchthat=439;e.parenleft=333;e.parenright=333;e.asteriskmath=500;e.plus=549;e.comma=250;e.minus=549;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=549;e.equal=549;e.greater=549;e.question=444;e.congruent=549;e.Alpha=722;e.Beta=667;e.Chi=722;e.Delta=612;e.Epsilon=611;e.Phi=763;e.Gamma=603;e.Eta=722;e.Iota=333;e.theta1=631;e.Kappa=722;e.Lambda=686;e.Mu=889;e.Nu=722;e.Omicron=722;e.Pi=768;e.Theta=741;e.Rho=556;e.Sigma=592;e.Tau=611;e.Upsilon=690;e.sigma1=439;e.Omega=768;e.Xi=645;e.Psi=795;e.Zeta=611;e.bracketleft=333;e.therefore=863;e.bracketright=333;e.perpendicular=658;e.underscore=500;e.radicalex=500;e.alpha=631;e.beta=549;e.chi=549;e.delta=494;e.epsilon=439;e.phi=521;e.gamma=411;e.eta=603;e.iota=329;e.phi1=603;e.kappa=549;e.lambda=549;e.mu=576;e.nu=521;e.omicron=549;e.pi=549;e.theta=521;e.rho=549;e.sigma=603;e.tau=439;e.upsilon=576;e.omega1=713;e.omega=686;e.xi=493;e.psi=686;e.zeta=494;e.braceleft=480;e.bar=200;e.braceright=480;e.similar=549;e.Euro=750;e.Upsilon1=620;e.minute=247;e.lessequal=549;e.fraction=167;e.infinity=713;e.florin=500;e.club=753;e.diamond=753;e.heart=753;e.spade=753;e.arrowboth=1042;e.arrowleft=987;e.arrowup=603;e.arrowright=987;e.arrowdown=603;e.degree=400;e.plusminus=549;e.second=411;e.greaterequal=549;e.multiply=549;e.proportional=713;e.partialdiff=494;e.bullet=460;e.divide=549;e.notequal=549;e.equivalence=549;e.approxequal=549;e.ellipsis=1e3;e.arrowvertex=603;e.arrowhorizex=1e3;e.carriagereturn=658;e.aleph=823;e.Ifraktur=686;e.Rfraktur=795;e.weierstrass=987;e.circlemultiply=768;e.circleplus=768;e.emptyset=823;e.intersection=768;e.union=768;e.propersuperset=713;e.reflexsuperset=713;e.notsubset=713;e.propersubset=713;e.reflexsubset=713;e.element=713;e.notelement=713;e.angle=768;e.gradient=713;e.registerserif=790;e.copyrightserif=790;e.trademarkserif=890;e.product=823;e.radical=549;e.dotmath=250;e.logicalnot=713;e.logicaland=603;e.logicalor=603;e.arrowdblboth=1042;e.arrowdblleft=987;e.arrowdblup=603;e.arrowdblright=987;e.arrowdbldown=603;e.lozenge=494;e.angleleft=329;e.registersans=790;e.copyrightsans=790;e.trademarksans=786;e.summation=713;e.parenlefttp=384;e.parenleftex=384;e.parenleftbt=384;e.bracketlefttp=384;e.bracketleftex=384;e.bracketleftbt=384;e.bracelefttp=494;e.braceleftmid=494;e.braceleftbt=494;e.braceex=494;e.angleright=329;e.integral=274;e.integraltp=686;e.integralex=686;e.integralbt=686;e.parenrighttp=384;e.parenrightex=384;e.parenrightbt=384;e.bracketrighttp=384;e.bracketrightex=384;e.bracketrightbt=384;e.bracerighttp=494;e.bracerightmid=494;e.bracerightbt=494;e.apple=790}));e["Times-Roman"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=408;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=564;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=564;e.equal=564;e.greater=564;e.question=444;e.at=921;e.A=722;e.B=667;e.C=667;e.D=722;e.E=611;e.F=556;e.G=722;e.H=722;e.I=333;e.J=389;e.K=722;e.L=611;e.M=889;e.N=722;e.O=722;e.P=556;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=722;e.W=944;e.X=722;e.Y=722;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=469;e.underscore=500;e.quoteleft=333;e.a=444;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=500;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=500;e.o=500;e.p=500;e.q=500;e.r=333;e.s=389;e.t=278;e.u=500;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=480;e.bar=200;e.braceright=480;e.asciitilde=541;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=180;e.quotedblleft=444;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=453;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=444;e.quotedblright=444;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=444;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=889;e.ordfeminine=276;e.Lslash=611;e.Oslash=722;e.OE=889;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=444;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=722;e.divide=564;e.Yacute=722;e.Acircumflex=722;e.aacute=444;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=444;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=444;e.Ncommaaccent=722;e.lacute=278;e.agrave=444;e.Tcommaaccent=611;e.Cacute=667;e.atilde=444;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=444;e.Amacron=722;e.rcaron=333;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=556;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=588;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=722;e.Abreve=722;e.multiply=564;e.uacute=500;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=444;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=722;e.Iacute=333;e.plusminus=564;e.brokenbar=200;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=333;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=326;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=444;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=344;e.Kcommaaccent=722;e.Lacute=611;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=722;e.zdotaccent=444;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=500;e.minus=564;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=564;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Bold"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=1e3;e.ampersand=833;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=930;e.A=722;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=778;e.I=389;e.J=500;e.K=778;e.L=667;e.M=944;e.N=722;e.O=778;e.P=611;e.Q=778;e.R=722;e.S=556;e.T=667;e.U=722;e.V=722;e.W=1e3;e.X=722;e.Y=722;e.Z=667;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=581;e.underscore=500;e.quoteleft=333;e.a=500;e.b=556;e.c=444;e.d=556;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=333;e.k=556;e.l=278;e.m=833;e.n=556;e.o=500;e.p=556;e.q=556;e.r=444;e.s=389;e.t=333;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=394;e.bar=220;e.braceright=394;e.asciitilde=520;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=540;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=300;e.Lslash=667;e.Oslash=778;e.OE=1e3;e.ordmasculine=330;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=556;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=722;e.divide=570;e.Yacute=722;e.Acircumflex=722;e.aacute=500;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=667;e.Cacute=722;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=500;e.Amacron=722;e.rcaron=444;e.ccedilla=444;e.Zdotaccent=667;e.Thorn=611;e.Omacron=778;e.Racute=722;e.Sacute=556;e.dcaron=672;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=570;e.uacute=556;e.Tcaron=667;e.partialdiff=494;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=778;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=444;e.omacron=500;e.Zacute=667;e.Zcaron=667;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=416;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=778;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=300;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=556;e.threequarters=750;e.Scedilla=556;e.lcaron=394;e.Kcommaaccent=778;e.Lacute=667;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=667;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=778;e.degree=400;e.ograve=500;e.Ccaron=722;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=444;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=722;e.Lcommaaccent=667;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=444;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=556;e.minus=570;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=333;e.logicalnot=570;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-BoldItalic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=389;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=832;e.A=667;e.B=667;e.C=667;e.D=722;e.E=667;e.F=667;e.G=722;e.H=778;e.I=389;e.J=500;e.K=667;e.L=611;e.M=889;e.N=722;e.O=722;e.P=611;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=667;e.W=889;e.X=667;e.Y=611;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=570;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=556;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=556;e.v=444;e.w=667;e.x=500;e.y=444;e.z=389;e.braceleft=348;e.bar=220;e.braceright=348;e.asciitilde=570;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=500;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=944;e.ordfeminine=266;e.Lslash=611;e.Oslash=722;e.OE=944;e.ordmasculine=300;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=611;e.divide=570;e.Yacute=611;e.Acircumflex=667;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=611;e.Cacute=667;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=556;e.acircumflex=500;e.Amacron=667;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=611;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=608;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=722;e.Agrave=667;e.Abreve=667;e.multiply=570;e.uacute=556;e.Tcaron=611;e.partialdiff=494;e.ydieresis=444;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=722;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=366;e.eogonek=444;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=576;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=382;e.Kcommaaccent=667;e.Lacute=611;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=722;e.zdotaccent=389;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=500;e.minus=606;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=606;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Italic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=420;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=675;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=675;e.equal=675;e.greater=675;e.question=500;e.at=920;e.A=611;e.B=611;e.C=667;e.D=722;e.E=611;e.F=611;e.G=722;e.H=722;e.I=333;e.J=444;e.K=667;e.L=556;e.M=833;e.N=667;e.O=722;e.P=611;e.Q=722;e.R=611;e.S=500;e.T=556;e.U=722;e.V=611;e.W=833;e.X=611;e.Y=556;e.Z=556;e.bracketleft=389;e.backslash=278;e.bracketright=389;e.asciicircum=422;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=278;e.g=500;e.h=500;e.i=278;e.j=278;e.k=444;e.l=278;e.m=722;e.n=500;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=500;e.v=444;e.w=667;e.x=444;e.y=444;e.z=389;e.braceleft=400;e.bar=275;e.braceright=400;e.asciitilde=541;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=214;e.quotedblleft=556;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=523;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=556;e.quotedblright=556;e.guillemotright=500;e.ellipsis=889;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=889;e.AE=889;e.ordfeminine=276;e.Lslash=556;e.Oslash=722;e.OE=944;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=667;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=500;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=556;e.divide=675;e.Yacute=556;e.Acircumflex=611;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=500;e.Ncommaaccent=667;e.lacute=278;e.agrave=500;e.Tcommaaccent=556;e.Cacute=667;e.atilde=500;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=611;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=500;e.Amacron=611;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=556;e.Thorn=611;e.Omacron=722;e.Racute=611;e.Sacute=500;e.dcaron=544;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=611;e.Abreve=611;e.multiply=675;e.uacute=500;e.Tcaron=556;e.partialdiff=476;e.ydieresis=444;e.Nacute=667;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=667;e.Iacute=333;e.plusminus=675;e.brokenbar=275;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=389;e.omacron=500;e.Zacute=556;e.Zcaron=556;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=300;e.eogonek=444;e.Uogonek=722;e.Aacute=611;e.Adieresis=611;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=500;e.lcaron=300;e.Kcommaaccent=667;e.Lacute=556;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=500;e.Scommaaccent=500;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=667;e.otilde=500;e.Rcommaaccent=611;e.Lcommaaccent=556;e.Atilde=611;e.Aogonek=611;e.Aring=611;e.Otilde=722;e.zdotaccent=389;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=444;e.minus=675;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=675;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e.ZapfDingbats=getLookupTableFactory((function(e){e.space=278;e.a1=974;e.a2=961;e.a202=974;e.a3=980;e.a4=719;e.a5=789;e.a119=790;e.a118=791;e.a117=690;e.a11=960;e.a12=939;e.a13=549;e.a14=855;e.a15=911;e.a16=933;e.a105=911;e.a17=945;e.a18=974;e.a19=755;e.a20=846;e.a21=762;e.a22=761;e.a23=571;e.a24=677;e.a25=763;e.a26=760;e.a27=759;e.a28=754;e.a6=494;e.a7=552;e.a8=537;e.a9=577;e.a10=692;e.a29=786;e.a30=788;e.a31=788;e.a32=790;e.a33=793;e.a34=794;e.a35=816;e.a36=823;e.a37=789;e.a38=841;e.a39=823;e.a40=833;e.a41=816;e.a42=831;e.a43=923;e.a44=744;e.a45=723;e.a46=749;e.a47=790;e.a48=792;e.a49=695;e.a50=776;e.a51=768;e.a52=792;e.a53=759;e.a54=707;e.a55=708;e.a56=682;e.a57=701;e.a58=826;e.a59=815;e.a60=789;e.a61=789;e.a62=707;e.a63=687;e.a64=696;e.a65=689;e.a66=786;e.a67=787;e.a68=713;e.a69=791;e.a70=785;e.a71=791;e.a72=873;e.a73=761;e.a74=762;e.a203=762;e.a75=759;e.a204=759;e.a76=892;e.a77=892;e.a78=788;e.a79=784;e.a81=438;e.a82=138;e.a83=277;e.a84=415;e.a97=392;e.a98=392;e.a99=668;e.a100=668;e.a89=390;e.a90=390;e.a93=317;e.a94=317;e.a91=276;e.a92=276;e.a205=509;e.a85=509;e.a206=410;e.a86=410;e.a87=234;e.a88=234;e.a95=334;e.a96=334;e.a101=732;e.a102=544;e.a103=544;e.a104=910;e.a106=667;e.a107=760;e.a108=760;e.a112=776;e.a111=595;e.a110=694;e.a109=626;e.a120=788;e.a121=788;e.a122=788;e.a123=788;e.a124=788;e.a125=788;e.a126=788;e.a127=788;e.a128=788;e.a129=788;e.a130=788;e.a131=788;e.a132=788;e.a133=788;e.a134=788;e.a135=788;e.a136=788;e.a137=788;e.a138=788;e.a139=788;e.a140=788;e.a141=788;e.a142=788;e.a143=788;e.a144=788;e.a145=788;e.a146=788;e.a147=788;e.a148=788;e.a149=788;e.a150=788;e.a151=788;e.a152=788;e.a153=788;e.a154=788;e.a155=788;e.a156=788;e.a157=788;e.a158=788;e.a159=788;e.a160=894;e.a161=838;e.a163=1016;e.a164=458;e.a196=748;e.a165=924;e.a192=748;e.a166=918;e.a167=927;e.a168=928;e.a169=928;e.a170=834;e.a171=873;e.a172=828;e.a173=924;e.a162=924;e.a174=917;e.a175=930;e.a176=931;e.a177=463;e.a178=883;e.a179=836;e.a193=836;e.a180=867;e.a199=867;e.a181=696;e.a200=696;e.a182=874;e.a201=874;e.a183=760;e.a184=946;e.a197=771;e.a185=865;e.a194=771;e.a198=888;e.a186=967;e.a195=888;e.a187=831;e.a188=873;e.a189=927;e.a190=970;e.a191=918}))})),sa=getLookupTableFactory((function(e){e.Courier={ascent:629,descent:-157,capHeight:562,xHeight:-426};e["Courier-Bold"]={ascent:629,descent:-157,capHeight:562,xHeight:439};e["Courier-Oblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e["Courier-BoldOblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e.Helvetica={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-Bold"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Helvetica-Oblique"]={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-BoldOblique"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Times-Roman"]={ascent:683,descent:-217,capHeight:662,xHeight:450};e["Times-Bold"]={ascent:683,descent:-217,capHeight:676,xHeight:461};e["Times-Italic"]={ascent:683,descent:-217,capHeight:653,xHeight:441};e["Times-BoldItalic"]={ascent:683,descent:-217,capHeight:669,xHeight:462};e.Symbol={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN};e.ZapfDingbats={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN}}));class GlyfTable{constructor({glyfTable:e,isGlyphLocationsLong:t,locaTable:i,numGlyphs:a}){this.glyphs=[];const s=new DataView(i.buffer,i.byteOffset,i.byteLength),r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t?4:2;let g=t?s.getUint32(0):2*s.getUint16(0),o=0;for(let e=0;e<a;e++){o+=n;const e=t?s.getUint32(o):2*s.getUint16(o);if(e===g){this.glyphs.push(new Glyph({}));continue}const i=Glyph.parse(g,r);this.glyphs.push(i);g=e}}getSize(){return this.glyphs.reduce(((e,t)=>e+(t.getSize()+3&-4)),0)}write(){const e=this.getSize(),t=new DataView(new ArrayBuffer(e)),i=e>131070,a=i?4:2,s=new DataView(new ArrayBuffer((this.glyphs.length+1)*a));i?s.setUint32(0,0):s.setUint16(0,0);let r=0,n=0;for(const e of this.glyphs){r+=e.write(r,t);r=r+3&-4;n+=a;i?s.setUint32(n,r):s.setUint16(n,r>>1)}return{isLocationLong:i,loca:new Uint8Array(s.buffer),glyf:new Uint8Array(t.buffer)}}scale(e){for(let t=0,i=this.glyphs.length;t<i;t++)this.glyphs[t].scale(e[t])}}class Glyph{constructor({header:e=null,simple:t=null,composites:i=null}){this.header=e;this.simple=t;this.composites=i}static parse(e,t){const[i,a]=GlyphHeader.parse(e,t);e+=i;if(a.numberOfContours<0){const i=[];for(;;){const[a,s]=CompositeGlyph.parse(e,t);e+=a;i.push(s);if(!(32&s.flags))break}return new Glyph({header:a,composites:i})}const s=SimpleGlyph.parse(e,t,a.numberOfContours);return new Glyph({header:a,simple:s})}getSize(){if(!this.header)return 0;const e=this.simple?this.simple.getSize():this.composites.reduce(((e,t)=>e+t.getSize()),0);return this.header.getSize()+e}write(e,t){if(!this.header)return 0;const i=e;e+=this.header.write(e,t);if(this.simple)e+=this.simple.write(e,t);else for(const i of this.composites)e+=i.write(e,t);return e-i}scale(e){if(!this.header)return;const t=(this.header.xMin+this.header.xMax)/2;this.header.scale(t,e);if(this.simple)this.simple.scale(t,e);else for(const i of this.composites)i.scale(t,e)}}class GlyphHeader{constructor({numberOfContours:e,xMin:t,yMin:i,xMax:a,yMax:s}){this.numberOfContours=e;this.xMin=t;this.yMin=i;this.xMax=a;this.yMax=s}static parse(e,t){return[10,new GlyphHeader({numberOfContours:t.getInt16(e),xMin:t.getInt16(e+2),yMin:t.getInt16(e+4),xMax:t.getInt16(e+6),yMax:t.getInt16(e+8)})]}getSize(){return 10}write(e,t){t.setInt16(e,this.numberOfContours);t.setInt16(e+2,this.xMin);t.setInt16(e+4,this.yMin);t.setInt16(e+6,this.xMax);t.setInt16(e+8,this.yMax);return 10}scale(e,t){this.xMin=Math.round(e+(this.xMin-e)*t);this.xMax=Math.round(e+(this.xMax-e)*t)}}class Contour{constructor({flags:e,xCoordinates:t,yCoordinates:i}){this.xCoordinates=t;this.yCoordinates=i;this.flags=e}}class SimpleGlyph{constructor({contours:e,instructions:t}){this.contours=e;this.instructions=t}static parse(e,t,i){const a=[];for(let s=0;s<i;s++){const i=t.getUint16(e);e+=2;a.push(i)}const s=a[i-1]+1,r=t.getUint16(e);e+=2;const n=new Uint8Array(t).slice(e,e+r);e+=r;const g=[];for(let i=0;i<s;e++,i++){let a=t.getUint8(e);g.push(a);if(8&a){const s=t.getUint8(++e);a^=8;for(let e=0;e<s;e++)g.push(a);i+=s}}const o=[];let c=[],C=[],h=[];const l=[];let Q=0,E=0;for(let i=0;i<s;i++){const s=g[i];if(2&s){const i=t.getUint8(e++);E+=16&s?i:-i;c.push(E)}else if(16&s)c.push(E);else{E+=t.getInt16(e);e+=2;c.push(E)}if(a[Q]===i){Q++;o.push(c);c=[]}}E=0;Q=0;for(let i=0;i<s;i++){const s=g[i];if(4&s){const i=t.getUint8(e++);E+=32&s?i:-i;C.push(E)}else if(32&s)C.push(E);else{E+=t.getInt16(e);e+=2;C.push(E)}h.push(1&s|64&s);if(a[Q]===i){c=o[Q];Q++;l.push(new Contour({flags:h,xCoordinates:c,yCoordinates:C}));C=[];h=[]}}return new SimpleGlyph({contours:l,instructions:n})}getSize(){let e=2*this.contours.length+2+this.instructions.length,t=0,i=0;for(const a of this.contours){e+=a.flags.length;for(let s=0,r=a.xCoordinates.length;s<r;s++){const r=a.xCoordinates[s],n=a.yCoordinates[s];let g=Math.abs(r-t);g>255?e+=2:g>0&&(e+=1);t=r;g=Math.abs(n-i);g>255?e+=2:g>0&&(e+=1);i=n}}return e}write(e,t){const i=e,a=[],s=[],r=[];let n=0,g=0;for(const i of this.contours){for(let e=0,t=i.xCoordinates.length;e<t;e++){let t=i.flags[e];const o=i.xCoordinates[e];let c=o-n;if(0===c){t|=16;a.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?18:2;a.push(e)}else a.push(c)}n=o;const C=i.yCoordinates[e];c=C-g;if(0===c){t|=32;s.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?36:4;s.push(e)}else s.push(c)}g=C;r.push(t)}t.setUint16(e,a.length-1);e+=2}t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}for(const i of r)t.setUint8(e++,i);for(let i=0,s=a.length;i<s;i++){const s=a[i],n=r[i];if(2&n)t.setUint8(e++,s);else if(!(16&n)){t.setInt16(e,s);e+=2}}for(let i=0,a=s.length;i<a;i++){const a=s[i],n=r[i];if(4&n)t.setUint8(e++,a);else if(!(32&n)){t.setInt16(e,a);e+=2}}return e-i}scale(e,t){for(const i of this.contours)if(0!==i.xCoordinates.length)for(let a=0,s=i.xCoordinates.length;a<s;a++)i.xCoordinates[a]=Math.round(e+(i.xCoordinates[a]-e)*t)}}class CompositeGlyph{constructor({flags:e,glyphIndex:t,argument1:i,argument2:a,transf:s,instructions:r}){this.flags=e;this.glyphIndex=t;this.argument1=i;this.argument2=a;this.transf=s;this.instructions=r}static parse(e,t){const i=e,a=[];let s=t.getUint16(e);const r=t.getUint16(e+2);e+=4;let n,g;if(1&s){if(2&s){n=t.getInt16(e);g=t.getInt16(e+2)}else{n=t.getUint16(e);g=t.getUint16(e+2)}e+=4;s^=1}else{if(2&s){n=t.getInt8(e);g=t.getInt8(e+1)}else{n=t.getUint8(e);g=t.getUint8(e+1)}e+=2}if(8&s){a.push(t.getUint16(e));e+=2}else if(64&s){a.push(t.getUint16(e),t.getUint16(e+2));e+=4}else if(128&s){a.push(t.getUint16(e),t.getUint16(e+2),t.getUint16(e+4),t.getUint16(e+6));e+=8}let o=null;if(256&s){const i=t.getUint16(e);e+=2;o=new Uint8Array(t).slice(e,e+i);e+=i}return[e-i,new CompositeGlyph({flags:s,glyphIndex:r,argument1:n,argument2:g,transf:a,instructions:o})]}getSize(){let e=4+2*this.transf.length;256&this.flags&&(e+=2+this.instructions.length);e+=2;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(e+=2):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(e+=2);return e}write(e,t){const i=e;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(this.flags|=1):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(this.flags|=1);t.setUint16(e,this.flags);t.setUint16(e+2,this.glyphIndex);e+=4;if(1&this.flags){if(2&this.flags){t.setInt16(e,this.argument1);t.setInt16(e+2,this.argument2)}else{t.setUint16(e,this.argument1);t.setUint16(e+2,this.argument2)}e+=4}else{t.setUint8(e,this.argument1);t.setUint8(e+1,this.argument2);e+=2}if(256&this.flags){t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}}return e-i}scale(e,t){}}function writeInt16(e,t,i){e[t]=i>>8&255;e[t+1]=255&i}function writeInt32(e,t,i){e[t]=i>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}function writeData(e,t,i){if(i instanceof Uint8Array)e.set(i,t);else if("string"==typeof i)for(let a=0,s=i.length;a<s;a++)e[t++]=255&i.charCodeAt(a);else for(const a of i)e[t++]=255&a}class OpenTypeFileBuilder{constructor(e){this.sfnt=e;this.tables=Object.create(null)}static getSearchParams(e,t){let i=1,a=0;for(;(i^e)>i;){i<<=1;a++}const s=i*t;return{range:s,entry:a,rangeShift:t*e-s}}toArray(){let e=this.sfnt;const t=this.tables,i=Object.keys(t);i.sort();const a=i.length;let s,r,n,g,o,c=12+16*a;const C=[c];for(s=0;s<a;s++){g=t[i[s]];c+=(g.length+3&-4)>>>0;C.push(c)}const h=new Uint8Array(c);for(s=0;s<a;s++){g=t[i[s]];writeData(h,C[s],g)}"true"===e&&(e=string32(65536));h[0]=255&e.charCodeAt(0);h[1]=255&e.charCodeAt(1);h[2]=255&e.charCodeAt(2);h[3]=255&e.charCodeAt(3);writeInt16(h,4,a);const l=OpenTypeFileBuilder.getSearchParams(a,16);writeInt16(h,6,l.range);writeInt16(h,8,l.entry);writeInt16(h,10,l.rangeShift);c=12;for(s=0;s<a;s++){o=i[s];h[c]=255&o.charCodeAt(0);h[c+1]=255&o.charCodeAt(1);h[c+2]=255&o.charCodeAt(2);h[c+3]=255&o.charCodeAt(3);let e=0;for(r=C[s],n=C[s+1];r<n;r+=4){e=e+readUint32(h,r)>>>0}writeInt32(h,c+4,e);writeInt32(h,c+8,C[s]);writeInt32(h,c+12,t[o].length);c+=16}return h}addTable(e,t){if(e in this.tables)throw new Error("Table "+e+" already exists");this.tables[e]=t}}const ra=[4],na=[5],ga=[6],oa=[7],Ia=[8],ca=[12,35],Ca=[14],ha=[21],Ba=[22],la=[30],Qa=[31];class Type1CharString{constructor(){this.width=0;this.lsb=0;this.flexing=!1;this.output=[];this.stack=[]}convert(e,t,i){const a=e.length;let s,r,n,g=!1;for(let o=0;o<a;o++){let a=e[o];if(a<32){12===a&&(a=(a<<8)+e[++o]);switch(a){case 1:case 3:case 9:case 3072:case 3073:case 3074:case 3105:this.stack=[];break;case 4:if(this.flexing){if(this.stack.length<1){g=!0;break}const e=this.stack.pop();this.stack.push(0,e);break}g=this.executeCommand(1,ra);break;case 5:g=this.executeCommand(2,na);break;case 6:g=this.executeCommand(1,ga);break;case 7:g=this.executeCommand(1,oa);break;case 8:g=this.executeCommand(6,Ia);break;case 10:if(this.stack.length<1){g=!0;break}n=this.stack.pop();if(!t[n]){g=!0;break}g=this.convert(t[n],t,i);break;case 11:return g;case 13:if(this.stack.length<2){g=!0;break}s=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r);g=this.executeCommand(2,Ba);break;case 14:this.output.push(Ca[0]);break;case 21:if(this.flexing)break;g=this.executeCommand(2,ha);break;case 22:if(this.flexing){this.stack.push(0);break}g=this.executeCommand(1,Ba);break;case 30:g=this.executeCommand(4,la);break;case 31:g=this.executeCommand(4,Qa);break;case 3078:if(i){const e=this.stack.at(-5);this.seac=this.stack.splice(-4,4);this.seac[0]+=this.lsb-e;g=this.executeCommand(0,Ca)}else g=this.executeCommand(4,Ca);break;case 3079:if(this.stack.length<4){g=!0;break}this.stack.pop();s=this.stack.pop();const e=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r,e);g=this.executeCommand(3,ha);break;case 3084:if(this.stack.length<2){g=!0;break}const o=this.stack.pop(),c=this.stack.pop();this.stack.push(c/o);break;case 3088:if(this.stack.length<2){g=!0;break}n=this.stack.pop();const C=this.stack.pop();if(0===n&&3===C){const e=this.stack.splice(-17,17);this.stack.push(e[2]+e[0],e[3]+e[1],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14]);g=this.executeCommand(13,ca,!0);this.flexing=!1;this.stack.push(e[15],e[16])}else 1===n&&0===C&&(this.flexing=!0);break;case 3089:break;default:warn('Unknown type 1 charstring command of "'+a+'"')}if(g)break}else{a<=246?a-=139:a=a<=250?256*(a-247)+e[++o]+108:a<=254?-256*(a-251)-e[++o]-108:(255&e[++o])<<24|(255&e[++o])<<16|(255&e[++o])<<8|(255&e[++o])<<0;this.stack.push(a)}}return g}executeCommand(e,t,i){const a=this.stack.length;if(e>a)return!0;const s=a-e;for(let e=s;e<a;e++){let t=this.stack[e];if(Number.isInteger(t))this.output.push(28,t>>8&255,255&t);else{t=65536*t|0;this.output.push(255,t>>24&255,t>>16&255,t>>8&255,255&t)}}this.output.push(...t);i?this.stack.splice(s,e):this.stack.length=0;return!1}}function isHexDigit(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function decrypt(e,t,i){if(i>=e.length)return new Uint8Array(0);let a,s,r=0|t;for(a=0;a<i;a++)r=52845*(e[a]+r)+22719&65535;const n=e.length-i,g=new Uint8Array(n);for(a=i,s=0;s<n;a++,s++){const t=e[a];g[s]=t^r>>8;r=52845*(t+r)+22719&65535}return g}function isSpecial(e){return 47===e||91===e||93===e||123===e||125===e||40===e||41===e}class Type1Parser{constructor(e,t,i){if(t){const t=e.getBytes(),i=!((isHexDigit(t[0])||isWhiteSpace(t[0]))&&isHexDigit(t[1])&&isHexDigit(t[2])&&isHexDigit(t[3])&&isHexDigit(t[4])&&isHexDigit(t[5])&&isHexDigit(t[6])&&isHexDigit(t[7]));e=new Stream(i?decrypt(t,55665,4):function decryptAscii(e,t,i){let a=0|t;const s=e.length,r=new Uint8Array(s>>>1);let n,g;for(n=0,g=0;n<s;n++){const t=e[n];if(!isHexDigit(t))continue;n++;let i;for(;n<s&&!isHexDigit(i=e[n]);)n++;if(n<s){const e=parseInt(String.fromCharCode(t,i),16);r[g++]=e^a>>8;a=52845*(e+a)+22719&65535}}return r.slice(i,g)}(t,55665,4))}this.seacAnalysisEnabled=!!i;this.stream=e;this.nextChar()}readNumberArray(){this.getToken();const e=[];for(;;){const t=this.getToken();if(null===t||"]"===t||"}"===t)break;e.push(parseFloat(t||0))}return e}readNumber(){const e=this.getToken();return parseFloat(e||0)}readInt(){const e=this.getToken();return 0|parseInt(e||0,10)}readBoolean(){return"true"===this.getToken()?1:0}nextChar(){return this.currentChar=this.stream.getByte()}prevChar(){this.stream.skip(-2);return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(-1===t)return null;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}if(isSpecial(t)){this.nextChar();return String.fromCharCode(t)}let i="";do{i+=String.fromCharCode(t);t=this.nextChar()}while(t>=0&&!isWhiteSpace(t)&&!isSpecial(t));return i}readCharStrings(e,t){return-1===t?e:decrypt(e,4330,t)}extractFontProgram(e){const t=this.stream,i=[],a=[],s=Object.create(null);s.lenIV=4;const r={subrs:[],charstrings:[],properties:{privateData:s}};let n,g,o,c;for(;null!==(n=this.getToken());)if("/"===n){n=this.getToken();switch(n){case"CharStrings":this.getToken();this.getToken();this.getToken();this.getToken();for(;;){n=this.getToken();if(null===n||"end"===n)break;if("/"!==n)continue;const e=this.getToken();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const i=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n?this.getToken():"/"===n&&this.prevChar();a.push({glyph:e,encoded:i})}break;case"Subrs":this.readInt();this.getToken();for(;"dup"===this.getToken();){const e=this.readInt();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const a=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n&&this.getToken();i[e]=a}break;case"BlueValues":case"OtherBlues":case"FamilyBlues":case"FamilyOtherBlues":const e=this.readNumberArray();e.length>0&&e.length,0;break;case"StemSnapH":case"StemSnapV":r.properties.privateData[n]=this.readNumberArray();break;case"StdHW":case"StdVW":r.properties.privateData[n]=this.readNumberArray()[0];break;case"BlueShift":case"lenIV":case"BlueFuzz":case"BlueScale":case"LanguageGroup":r.properties.privateData[n]=this.readNumber();break;case"ExpansionFactor":r.properties.privateData[n]=this.readNumber()||.06;break;case"ForceBold":r.properties.privateData[n]=this.readBoolean()}}for(const{encoded:t,glyph:s}of a){const a=new Type1CharString,n=a.convert(t,i,this.seacAnalysisEnabled);let g=a.output;n&&(g=[14]);const o={glyphName:s,charstring:g,width:a.width,lsb:a.lsb,seac:a.seac};".notdef"===s?r.charstrings.unshift(o):r.charstrings.push(o);if(e.builtInEncoding){const t=e.builtInEncoding.indexOf(s);t>-1&&void 0===e.widths[t]&&t>=e.firstChar&&t<=e.lastChar&&(e.widths[t]=a.width)}}return r}extractFontHeader(e){let t;for(;null!==(t=this.getToken());)if("/"===t){t=this.getToken();switch(t){case"FontMatrix":const i=this.readNumberArray();e.fontMatrix=i;break;case"Encoding":const a=this.getToken();let s;if(/^\d+$/.test(a)){s=[];const e=0|parseInt(a,10);this.getToken();for(let i=0;i<e;i++){t=this.getToken();for(;"dup"!==t&&"def"!==t;){t=this.getToken();if(null===t)return}if("def"===t)break;const e=this.readInt();this.getToken();const i=this.getToken();s[e]=i;this.getToken()}}else s=getEncoding(a);e.builtInEncoding=s;break;case"FontBBox":const r=this.readNumberArray();e.ascent=Math.max(r[3],r[1]);e.descent=Math.min(r[1],r[3]);e.ascentScaled=!0}}}}function findBlock(e,t,i){const a=e.length,s=t.length,r=a-s;let n=i,g=!1;for(;n<r;){let i=0;for(;i<s&&e[n+i]===t[i];)i++;if(i>=s){n+=i;for(;n<a&&isWhiteSpace(e[n]);)n++;g=!0;break}n++}return{found:g,length:n}}class Type1Font{constructor(e,t,i){let a=i.length1,s=i.length2,r=t.peekBytes(6);const n=128===r[0]&&1===r[1];if(n){t.skip(6);a=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const g=function getHeaderBlock(e,t){const i=[101,101,120,101,99],a=e.pos;let s,r,n,g;try{s=e.getBytes(t);r=s.length}catch{}if(r===t){n=findBlock(s,i,t-2*i.length);if(n.found&&n.length===t)return{stream:new Stream(s),length:t}}warn('Invalid "Length1" property in Type1 font -- trying to recover.');e.pos=a;for(;;){n=findBlock(e.peekBytes(2048),i,0);if(0===n.length)break;e.pos+=n.length;if(n.found){g=e.pos-a;break}}e.pos=a;if(g)return{stream:new Stream(e.getBytes(g)),length:g};warn('Unable to recover "Length1" property in Type1 font -- using as is.');return{stream:new Stream(e.getBytes(t)),length:t}}(t,a);new Type1Parser(g.stream,!1,Ti).extractFontHeader(i);if(n){r=t.getBytes(6);s=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const o=function getEexecBlock(e,t){const i=e.getBytes();if(0===i.length)throw new FormatError("getEexecBlock - no font program found.");return{stream:new Stream(i),length:i.length}}(t),c=new Type1Parser(o.stream,!0,Ti).extractFontProgram(i);for(const e in c.properties)i[e]=c.properties[e];const C=c.charstrings,h=this.getType2Charstrings(C),l=this.getType2Subrs(c.subrs);this.charstrings=C;this.data=this.wrap(e,h,this.charstrings,l,i);this.seacs=this.getSeacs(c.charstrings)}get numGlyphs(){return this.charstrings.length+1}getCharset(){const e=[".notdef"];for(const{glyphName:t}of this.charstrings)e.push(t);return e}getGlyphMapping(e){const t=this.charstrings;if(e.composite){const i=Object.create(null);for(let a=0,s=t.length;a<s;a++){i[e.cMap.charCodeOf(a)]=a+1}return i}const i=[".notdef"];let a,s;for(s=0;s<t.length;s++)i.push(t[s].glyphName);const r=e.builtInEncoding;if(r){a=Object.create(null);for(const e in r){s=i.indexOf(r[e]);s>=0&&(a[e]=s)}}return type1FontGlyphMapping(e,a,i)}hasGlyphId(e){if(e<0||e>=this.numGlyphs)return!1;if(0===e)return!0;return this.charstrings[e-1].charstring.length>0}getSeacs(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e[i];a.seac&&(t[i+1]=a.seac)}return t}getType2Charstrings(e){const t=[];for(const i of e)t.push(i.charstring);return t}getType2Subrs(e){let t=0;const i=e.length;t=i<1133?107:i<33769?1131:32768;const a=[];let s;for(s=0;s<t;s++)a.push([11]);for(s=0;s<i;s++)a.push(e[s]);return a}wrap(e,t,i,a,s){const r=new CFF;r.header=new CFFHeader(1,0,4,4);r.names=[e];const n=new CFFTopDict;n.setByName("version",391);n.setByName("Notice",392);n.setByName("FullName",393);n.setByName("FamilyName",394);n.setByName("Weight",395);n.setByName("Encoding",null);n.setByName("FontMatrix",s.fontMatrix);n.setByName("FontBBox",s.bbox);n.setByName("charset",null);n.setByName("CharStrings",null);n.setByName("Private",null);r.topDict=n;const g=new CFFStrings;g.add("Version 0.11");g.add("See original notice");g.add(e);g.add(e);g.add("Medium");r.strings=g;r.globalSubrIndex=new CFFIndex;const o=t.length,c=[".notdef"];let C,h;for(C=0;C<o;C++){const e=i[C].glyphName;-1===Fi.indexOf(e)&&g.add(e);c.push(e)}r.charset=new CFFCharset(!1,0,c);const l=new CFFIndex;l.add([139,14]);for(C=0;C<o;C++)l.add(t[C]);r.charStrings=l;const Q=new CFFPrivateDict;Q.setByName("Subrs",null);const E=["BlueValues","OtherBlues","FamilyBlues","FamilyOtherBlues","StemSnapH","StemSnapV","BlueShift","BlueFuzz","BlueScale","LanguageGroup","ExpansionFactor","ForceBold","StdHW","StdVW"];for(C=0,h=E.length;C<h;C++){const e=E[C];if(!(e in s.privateData))continue;const t=s.privateData[e];if(Array.isArray(t))for(let e=t.length-1;e>0;e--)t[e]-=t[e-1];Q.setByName(e,t)}r.topDict.privateDict=Q;const u=new CFFIndex;for(C=0,h=a.length;C<h;C++)u.add(a[C]);Q.subrsIndex=u;return new CFFCompiler(r).compile()}}const Ea=[[57344,63743],[1048576,1114109]],ua=1e3,da=["ascent","bbox","black","bold","charProcOperatorList","composite","cssFontInfo","data","defaultVMetrics","defaultWidth","descent","fallbackName","fontMatrix","isInvalidPDFjsFont","isType3Font","italic","loadedName","mimetype","missingFile","name","remeasure","subtype","systemFontInfo","type","vertical"],fa=["cMap","defaultEncoding","differences","isMonospace","isSerifFont","isSymbolicFont","seacMap","toFontChar","toUnicode","vmetrics","widths"];function adjustWidths(e){if(!e.fontMatrix)return;if(e.fontMatrix[0]===a[0])return;const t=.001/e.fontMatrix[0],i=e.widths;for(const e in i)i[e]*=t;e.defaultWidth*=t}function amendFallbackToUnicode(e){if(!e.fallbackToUnicode)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const t=[];for(const i in e.fallbackToUnicode)e.toUnicode.has(i)||(t[i]=e.fallbackToUnicode[i]);t.length>0&&e.toUnicode.amend(t)}class fonts_Glyph{constructor(e,t,i,a,s,r,n,g,o){this.originalCharCode=e;this.fontChar=t;this.unicode=i;this.accent=a;this.width=s;this.vmetric=r;this.operatorListId=n;this.isSpace=g;this.isInFont=o}get category(){return shadow(this,"category",function getCharUnicodeCategory(e){const t=Ki.get(e);if(t)return t;const i=e.match(vi),a={isWhitespace:!!i?.[1],isZeroWidthDiacritic:!!i?.[2],isInvisibleFormatMark:!!i?.[3]};Ki.set(e,a);return a}(this.unicode),!0)}}function int16(e,t){return(e<<8)+t}function writeSignedInt16(e,t,i){e[t+1]=i;e[t]=i>>>8}function signedInt16(e,t){const i=(e<<8)+t;return 32768&i?i-65536:i}function string16(e){return String.fromCharCode(e>>8&255,255&e)}function safeString16(e){e>32767?e=32767:e<-32768&&(e=-32768);return String.fromCharCode(e>>8&255,255&e)}function isTrueTypeCollectionFile(e){return"ttcf"===bytesToString(e.peekBytes(4))}function getFontFileType(e,{type:t,subtype:i,composite:a}){let s,r;if(function isTrueTypeFile(e){const t=e.peekBytes(4);return 65536===readUint32(t,0)||"true"===bytesToString(t)}(e)||isTrueTypeCollectionFile(e))s=a?"CIDFontType2":"TrueType";else if(function isOpenTypeFile(e){return"OTTO"===bytesToString(e.peekBytes(4))}(e))s=a?"CIDFontType2":"OpenType";else if(function isType1File(e){const t=e.peekBytes(2);return 37===t[0]&&33===t[1]||128===t[0]&&1===t[1]}(e))s=a?"CIDFontType0":"MMType1"===t?"MMType1":"Type1";else if(function isCFFFile(e){const t=e.peekBytes(4);return t[0]>=1&&t[3]>=1&&t[3]<=4}(e))if(a){s="CIDFontType0";r="CIDFontType0C"}else{s="MMType1"===t?"MMType1":"Type1";r="Type1C"}else{warn("getFontFileType: Unable to detect correct font file Type/Subtype.");s=t;r=i}return[s,r]}function applyStandardFontGlyphMap(e,t){for(const i in t)e[+i]=t[i]}function buildToFontChar(e,t,i){const a=[];let s;for(let i=0,r=e.length;i<r;i++){s=getUnicodeForGlyph(e[i],t);-1!==s&&(a[i]=s)}for(const e in i){s=getUnicodeForGlyph(i[e],t);-1!==s&&(a[+e]=s)}return a}function isMacNameRecord(e){return 1===e.platform&&0===e.encoding&&0===e.language}function isWinNameRecord(e){return 3===e.platform&&1===e.encoding&&1033===e.language}function convertCidString(e,t,i=!1){switch(t.length){case 1:return t.charCodeAt(0);case 2:return t.charCodeAt(0)<<8|t.charCodeAt(1)}const a=`Unsupported CID string (charCode ${e}): "${t}".`;if(i)throw new FormatError(a);warn(a);return t}function adjustMapping(e,t,i,a){const s=Object.create(null),r=new Map,n=[],g=new Set;let o=0;let c=Ea[o][0],C=Ea[o][1];for(const l in e){let Q=e[l];if(!t(Q))continue;if(c>C){o++;if(o>=Ea.length){warn("Ran out of space in font private use area.");break}c=Ea[o][0];C=Ea[o][1]}const E=c++;0===Q&&(Q=i);let u=a.get(l);"string"==typeof u&&(u=u.codePointAt(0));if(u&&!(h=u,Ea[0][0]<=h&&h<=Ea[0][1]||Ea[1][0]<=h&&h<=Ea[1][1])&&!g.has(Q)){r.set(u,Q);g.add(Q)}s[E]=Q;n[l]=E}var h;return{toFontChar:n,charCodeToGlyphId:s,toUnicodeExtraMap:r,nextAvailableFontCharCode:c}}function createCmapTable(e,t,i){const a=function getRanges(e,t,i){const a=[];for(const t in e)e[t]>=i||a.push({fontCharCode:0|t,glyphId:e[t]});if(t)for(const[e,s]of t)s>=i||a.push({fontCharCode:e,glyphId:s});0===a.length&&a.push({fontCharCode:0,glyphId:0});a.sort((function fontGetRangesSort(e,t){return e.fontCharCode-t.fontCharCode}));const s=[],r=a.length;for(let e=0;e<r;){const t=a[e].fontCharCode,i=[a[e].glyphId];++e;let n=t;for(;e<r&&n+1===a[e].fontCharCode;){i.push(a[e].glyphId);++n;++e;if(65535===n)break}s.push([t,n,i])}return s}(e,t,i),s=a.at(-1)[1]>65535?2:1;let r,n,g,o,c="\0\0"+string16(s)+"\0\0"+string32(4+8*s);for(r=a.length-1;r>=0&&!(a[r][0]<=65535);--r);const C=r+1;a[r][0]<65535&&65535===a[r][1]&&(a[r][1]=65534);const h=a[r][1]<65535?1:0,l=C+h,Q=OpenTypeFileBuilder.getSearchParams(l,2);let E,u,d,f,p="",m="",y="",w="",D="",b=0;for(r=0,n=C;r<n;r++){E=a[r];u=E[0];d=E[1];p+=string16(u);m+=string16(d);f=E[2];let e=!0;for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){e=!1;break}if(e){y+=string16(f[0]-u&65535);w+=string16(0)}else{const e=2*(l-r)+2*b;b+=d-u+1;y+=string16(0);w+=string16(e);for(g=0,o=f.length;g<o;++g)D+=string16(f[g])}}if(h>0){m+="ÿÿ";p+="ÿÿ";y+="\0";w+="\0\0"}const F="\0\0"+string16(2*l)+string16(Q.range)+string16(Q.entry)+string16(Q.rangeShift)+m+"\0\0"+p+y+w+D;let S="",k="";if(s>1){c+="\0\0\n"+string32(4+8*s+4+F.length);S="";for(r=0,n=a.length;r<n;r++){E=a[r];u=E[0];f=E[2];let e=f[0];for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){d=E[0]+g-1;S+=string32(u)+string32(d)+string32(e);u=d+1;e=f[g]}S+=string32(u)+string32(E[1])+string32(e)}k="\0\f\0\0"+string32(S.length+16)+"\0\0\0\0"+string32(S.length/12)}return c+"\0"+string16(F.length+4)+F+k+S}function createOS2Table(e,t,i){i||={unitsPerEm:0,yMax:0,yMin:0,ascent:0,descent:0};let a=0,s=0,r=0,n=0,g=null,o=0,c=-1;if(t){for(let e in t){e|=0;(g>e||!g)&&(g=e);o<e&&(o=e);c=getUnicodeRangeFor(e,c);if(c<32)a|=1<<c;else if(c<64)s|=1<<c-32;else if(c<96)r|=1<<c-64;else{if(!(c<123))throw new FormatError("Unicode ranges Bits > 123 are reserved for internal usage");n|=1<<c-96}}o>65535&&(o=65535)}else{g=0;o=255}const C=e.bbox||[0,0,0,0],h=i.unitsPerEm||(e.fontMatrix?1/Math.max(...e.fontMatrix.slice(0,4).map(Math.abs)):1e3),l=e.ascentScaled?1:h/ua,Q=i.ascent||Math.round(l*(e.ascent||C[3]));let E=i.descent||Math.round(l*(e.descent||C[1]));E>0&&e.descent>0&&C[1]<0&&(E=-E);const u=i.yMax||Q,d=-i.yMin||-E;return"\0$ô\0\0\0»\0\0\0»\0\0ß\x001\0\0\0\0"+String.fromCharCode(e.fixedPitch?9:0)+"\0\0\0\0\0\0"+string32(a)+string32(s)+string32(r)+string32(n)+"*21*"+string16(e.italicAngle?1:0)+string16(g||e.firstChar)+string16(o||e.lastChar)+string16(Q)+string16(E)+"\0d"+string16(u)+string16(d)+"\0\0\0\0\0\0\0\0"+string16(e.xHeight)+string16(e.capHeight)+string16(0)+string16(g||e.firstChar)+"\0"}function createPostTable(e){return"\0\0\0"+string32(Math.floor(65536*e.italicAngle))+"\0\0\0\0"+string32(e.fixedPitch?1:0)+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}function createPostscriptName(e){return e.replaceAll(/[^\x21-\x7E]|[[\](){}<>/%]/g,"").slice(0,63)}function createNameTable(e,t){t||(t=[[],[]]);const i=[t[0][0]||"Original licence",t[0][1]||e,t[0][2]||"Unknown",t[0][3]||"uniqueID",t[0][4]||e,t[0][5]||"Version 0.11",t[0][6]||createPostscriptName(e),t[0][7]||"Unknown",t[0][8]||"Unknown",t[0][9]||"Unknown"],a=[];let s,r,n,g,o;for(s=0,r=i.length;s<r;s++){o=t[1][s]||i[s];const e=[];for(n=0,g=o.length;n<g;n++)e.push(string16(o.charCodeAt(n)));a.push(e.join(""))}const c=[i,a],C=["\0","\0"],h=["\0\0","\0"],l=["\0\0","\t"],Q=i.length*C.length;let E="\0\0"+string16(Q)+string16(12*Q+6),u=0;for(s=0,r=C.length;s<r;s++){const e=c[s];for(n=0,g=e.length;n<g;n++){o=e[n];E+=C[s]+h[s]+l[s]+string16(n)+string16(o.length)+string16(u);u+=o.length}}E+=i.join("")+a.join("");return E}class Font{constructor(e,t,i){this.name=e;this.psName=null;this.mimetype=null;this.disableFontFace=!1;this.loadedName=i.loadedName;this.isType3Font=i.isType3Font;this.missingFile=!1;this.cssFontInfo=i.cssFontInfo;this._charsCache=Object.create(null);this._glyphCache=Object.create(null);let a=!!(i.flags&Oi);if(!a&&!i.isSimulatedFlags){const t=e.replaceAll(/[,_]/g,"-").split("-",1)[0],i=_i();for(const e of t.split("+"))if(i[e]){a=!0;break}}this.isSerifFont=a;this.isSymbolicFont=!!(i.flags&Pi);this.isMonospace=!!(i.flags&qi);let{type:s,subtype:r}=i;this.type=s;this.subtype=r;this.systemFontInfo=i.systemFontInfo;const n=e.match(/^InvalidPDFjsFont_(.*)_\d+$/);this.isInvalidPDFjsFont=!!n;this.isInvalidPDFjsFont?this.fallbackName=n[1]:this.isMonospace?this.fallbackName="monospace":this.isSerifFont?this.fallbackName="serif":this.fallbackName="sans-serif";if(this.systemFontInfo?.guessFallback){this.systemFontInfo.guessFallback=!1;this.systemFontInfo.css+=`,${this.fallbackName}`}this.differences=i.differences;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.composite=i.composite;this.cMap=i.cMap;this.capHeight=i.capHeight/ua;this.ascent=i.ascent/ua;this.descent=i.descent/ua;this.lineHeight=this.ascent-this.descent;this.fontMatrix=i.fontMatrix;this.bbox=i.bbox;this.defaultEncoding=i.defaultEncoding;this.toUnicode=i.toUnicode;this.toFontChar=[];if("Type3"===i.type){for(let e=0;e<256;e++)this.toFontChar[e]=this.differences[e]||i.defaultEncoding[e];return}this.cidEncoding=i.cidEncoding||"";this.vertical=!!i.vertical;if(this.vertical){this.vmetrics=i.vmetrics;this.defaultVMetrics=i.defaultVMetrics}if(!t||t.isEmpty){t&&warn('Font file is empty in "'+e+'" ('+this.loadedName+")");this.fallbackToSystemFont(i);return}[s,r]=getFontFileType(t,i);s===this.type&&r===this.subtype||info(`Inconsistent font file Type/SubType, expected: ${this.type}/${this.subtype} but found: ${s}/${r}.`);let g;try{switch(s){case"MMType1":info("MMType1 font ("+e+"), falling back to Type1.");case"Type1":case"CIDFontType0":this.mimetype="font/opentype";const a="Type1C"===r||"CIDFontType0C"===r?new CFFFont(t,i):new Type1Font(e,t,i);adjustWidths(i);g=this.convert(e,a,i);break;case"OpenType":case"TrueType":case"CIDFontType2":this.mimetype="font/opentype";g=this.checkAndRepair(e,t,i);if(this.isOpenType){adjustWidths(i);s="OpenType"}break;default:throw new FormatError(`Font ${s} is not supported`)}}catch(e){warn(e);this.fallbackToSystemFont(i);return}amendFallbackToUnicode(i);this.data=g;this.type=s;this.subtype=r;this.fontMatrix=i.fontMatrix;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.toUnicode=i.toUnicode;this.seacMap=i.seacMap}get renderer(){return shadow(this,"renderer",FontRendererFactory.create(this,Ti))}exportData(e=!1){const t=e?[...da,...fa]:da,i=Object.create(null);let a,s;for(a of t){s=this[a];void 0!==s&&(i[a]=s)}return i}fallbackToSystemFont(e){this.missingFile=!0;const{name:t,type:i}=this;let a=normalizeFontName(t);const s=Zi(),r=zi(),n=!!s[a],g=!(!r[a]||!s[r[a]]);a=s[a]||r[a]||a;const o=sa()[a];if(o){isNaN(this.ascent)&&(this.ascent=o.ascent/ua);isNaN(this.descent)&&(this.descent=o.descent/ua);isNaN(this.capHeight)&&(this.capHeight=o.capHeight/ua)}this.bold=/bold/gi.test(a);this.italic=/oblique|italic/gi.test(a);this.black=/Black/g.test(t);const c=/Narrow/g.test(t);this.remeasure=(!n||c)&&Object.keys(this.widths).length>0;if((n||g)&&"CIDFontType2"===i&&this.cidEncoding.startsWith("Identity-")){const i=e.cidToGidMap,a=[];applyStandardFontGlyphMap(a,Aa());/Arial-?Black/i.test(t)?applyStandardFontGlyphMap(a,ea()):/Calibri/i.test(t)&&applyStandardFontGlyphMap(a,ta());if(i){for(const e in a){const t=a[e];void 0!==i[t]&&(a[+e]=i[t])}i.length!==this.toUnicode.length&&e.hasIncludedToUnicodeMap&&this.toUnicode instanceof IdentityToUnicodeMap&&this.toUnicode.forEach((function(e,t){const s=a[e];void 0===i[s]&&(a[+e]=t)}))}this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(e,t){a[+e]=t}));this.toFontChar=a;this.toUnicode=new ToUnicodeMap(a)}else if(/Symbol/i.test(a))this.toFontChar=buildToFontChar(Di,Mi(),this.differences);else if(/Dingbats/i.test(a))this.toFontChar=buildToFontChar(bi,Hi(),this.differences);else if(n||g){const e=buildToFontChar(this.defaultEncoding,Mi(),this.differences);"CIDFontType2"!==i||this.cidEncoding.startsWith("Identity-")||this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(t,i){e[+t]=i}));this.toFontChar=e}else{const e=Mi(),i=[];this.toUnicode.forEach(((t,a)=>{if(!this.composite){const i=getUnicodeForGlyph(this.differences[t]||this.defaultEncoding[t],e);-1!==i&&(a=i)}i[+t]=a}));this.composite&&this.toUnicode instanceof IdentityToUnicodeMap&&/Tahoma|Verdana/i.test(t)&&applyStandardFontGlyphMap(i,Aa());this.toFontChar=i}amendFallbackToUnicode(e);this.loadedName=a.split("-",1)[0]}checkAndRepair(e,t,i){const a=["OS/2","cmap","head","hhea","hmtx","maxp","name","post","loca","glyf","fpgm","prep","cvt ","CFF "];function readTables(e,t){const i=Object.create(null);i["OS/2"]=null;i.cmap=null;i.head=null;i.hhea=null;i.hmtx=null;i.maxp=null;i.name=null;i.post=null;for(let s=0;s<t;s++){const t=readTableEntry(e);a.includes(t.tag)&&(0!==t.length&&(i[t.tag]=t))}return i}function readTableEntry(e){const t=e.getString(4),i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0,r=e.pos;e.pos=e.start||0;e.skip(a);const n=e.getBytes(s);e.pos=r;if("head"===t){n[8]=n[9]=n[10]=n[11]=0;n[17]|=32}return{tag:t,checksum:i,length:s,offset:a,data:n}}function readOpenTypeHeader(e){return{version:e.getString(4),numTables:e.getUint16(),searchRange:e.getUint16(),entrySelector:e.getUint16(),rangeShift:e.getUint16()}}function sanitizeGlyph(e,t,i,a,s,r){const n={length:0,sizeOfInstructions:0};if(t<0||t>=e.length||i>e.length||i-t<=12)return n;const g=e.subarray(t,i),o=signedInt16(g[2],g[3]),c=signedInt16(g[4],g[5]),C=signedInt16(g[6],g[7]),h=signedInt16(g[8],g[9]);if(o>C){writeSignedInt16(g,2,C);writeSignedInt16(g,6,o)}if(c>h){writeSignedInt16(g,4,h);writeSignedInt16(g,8,c)}const l=signedInt16(g[0],g[1]);if(l<0){if(l<-1)return n;a.set(g,s);n.length=g.length;return n}let Q,E=10,u=0;for(Q=0;Q<l;Q++){u=(g[E]<<8|g[E+1])+1;E+=2}const d=E,f=g[E]<<8|g[E+1];n.sizeOfInstructions=f;E+=2+f;const p=E;let m=0;for(Q=0;Q<u;Q++){const e=g[E++];192&e&&(g[E-1]=63&e);let t=2;2&e?t=1:16&e&&(t=0);let i=2;4&e?i=1:32&e&&(i=0);const a=t+i;m+=a;if(8&e){const e=g[E++];0===e&&(g[E-1]^=8);Q+=e;m+=e*a}}if(0===m)return n;let y=E+m;if(y>g.length)return n;if(!r&&f>0){a.set(g.subarray(0,d),s);a.set([0,0],s+d);a.set(g.subarray(p,y),s+d+2);y-=f;g.length-y>3&&(y=y+3&-4);n.length=y;return n}if(g.length-y>3){y=y+3&-4;a.set(g.subarray(0,y),s);n.length=y;return n}a.set(g,s);n.length=g.length;return n}function readNameTable(e){const i=(t.start||0)+e.offset;t.pos=i;const a=[[],[]],s=[],r=e.length,n=i+r;if(0!==t.getUint16()||r<6)return[a,s];const g=t.getUint16(),o=t.getUint16();let c,C;for(c=0;c<g&&t.pos+12<=n;c++){const e={platform:t.getUint16(),encoding:t.getUint16(),language:t.getUint16(),name:t.getUint16(),length:t.getUint16(),offset:t.getUint16()};(isMacNameRecord(e)||isWinNameRecord(e))&&s.push(e)}for(c=0,C=s.length;c<C;c++){const e=s[c];if(e.length<=0)continue;const r=i+o+e.offset;if(r+e.length>n)continue;t.pos=r;const g=e.name;if(e.encoding){let i="";for(let a=0,s=e.length;a<s;a+=2)i+=String.fromCharCode(t.getUint16());a[1][g]=i}else a[0][g]=t.getString(e.length)}return[a,s]}const s=[0,0,0,0,0,0,0,0,-2,-2,-2,-2,0,0,-2,-5,-1,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,-1,-1,1,-1,-999,0,1,0,-1,-2,0,-1,-2,-1,-1,0,-1,-1,0,0,-999,-999,-1,-1,-1,-1,-2,-999,-2,-2,-999,0,-2,-2,0,0,-2,0,-2,0,0,0,-2,-1,-1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,0,-999,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,-2,-999,-999,-999,-999,-999,-1,-1,-2,-2,0,0,0,0,-1,-1,-999,-2,-2,0,0,-1,-2,-2,0,0,0,-1,-1,-1,-2];function sanitizeTTProgram(e,t){let i,a,r,n,g,o=e.data,c=0,C=0,h=0;const l=[],Q=[],E=[];let u=t.tooComplexToFollowFunctions,d=!1,f=0,p=0;for(let e=o.length;c<e;){const e=o[c++];if(64===e){a=o[c++];if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(65===e){a=o[c++];if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(r<<8|o[c++])}}else if(176==(248&e)){a=e-176+1;if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(184==(248&e)){a=e-184+1;if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(signedInt16(r,o[c++]))}}else if(43!==e||u)if(44!==e||u){if(45===e)if(d){d=!1;C=c}else{g=Q.pop();if(!g){warn("TT: ENDF bad stack");t.hintsValid=!1;return}n=E.pop();o=g.data;c=g.i;t.functionsStackDeltas[n]=l.length-g.stackTop}else if(137===e){if(d||p){warn("TT: nested IDEFs not allowed");u=!0}d=!0;h=c}else if(88===e)++f;else if(27===e)p=f;else if(89===e){p===f&&(p=0);--f}else if(28===e&&!d&&!p){const e=l.at(-1);e>0&&(c+=e-1)}}else{if(d||p){warn("TT: nested FDEFs not allowed");u=!0}d=!0;h=c;n=l.pop();t.functionsDefined[n]={data:o,i:c}}else if(!d&&!p){n=l.at(-1);if(isNaN(n))info("TT: CALL empty stack (or invalid entry).");else{t.functionsUsed[n]=!0;if(n in t.functionsStackDeltas){const e=l.length+t.functionsStackDeltas[n];if(e<0){warn("TT: CALL invalid functions stack delta.");t.hintsValid=!1;return}l.length=e}else if(n in t.functionsDefined&&!E.includes(n)){Q.push({data:o,i:c,stackTop:l.length-1});E.push(n);g=t.functionsDefined[n];if(!g){warn("TT: CALL non-existent function");t.hintsValid=!1;return}o=g.data;c=g.i}}}if(!d&&!p){let t=0;e<=142?t=s[e]:e>=192&&e<=223?t=-1:e>=224&&(t=-2);if(e>=113&&e<=117){a=l.pop();isNaN(a)||(t=2*-a)}for(;t<0&&l.length>0;){l.pop();t++}for(;t>0;){l.push(NaN);t--}}}t.tooComplexToFollowFunctions=u;const m=[o];c>o.length&&m.push(new Uint8Array(c-o.length));if(h>C){warn("TT: complementing a missing function tail");m.push(new Uint8Array([34,45]))}!function foldTTTable(e,t){if(t.length>1){let i,a,s=0;for(i=0,a=t.length;i<a;i++)s+=t[i].length;s=s+3&-4;const r=new Uint8Array(s);let n=0;for(i=0,a=t.length;i<a;i++){r.set(t[i],n);n+=t[i].length}e.data=r;e.length=s}}(e,m)}let r,n,g,o;if(isTrueTypeCollectionFile(t=new Stream(new Uint8Array(t.getBytes())))){const e=function readTrueTypeCollectionData(e,t){const{numFonts:i,offsetTable:a}=function readTrueTypeCollectionHeader(e){const t=e.getString(4);assert("ttcf"===t,"Must be a TrueType Collection font.");const i=e.getUint16(),a=e.getUint16(),s=e.getInt32()>>>0,r=[];for(let t=0;t<s;t++)r.push(e.getInt32()>>>0);const n={ttcTag:t,majorVersion:i,minorVersion:a,numFonts:s,offsetTable:r};switch(i){case 1:return n;case 2:n.dsigTag=e.getInt32()>>>0;n.dsigLength=e.getInt32()>>>0;n.dsigOffset=e.getInt32()>>>0;return n}throw new FormatError(`Invalid TrueType Collection majorVersion: ${i}.`)}(e),s=t.split("+");let r;for(let n=0;n<i;n++){e.pos=(e.start||0)+a[n];const i=readOpenTypeHeader(e),g=readTables(e,i.numTables);if(!g.name)throw new FormatError('TrueType Collection font must contain a "name" table.');const[o]=readNameTable(g.name);for(let e=0,a=o.length;e<a;e++)for(let a=0,n=o[e].length;a<n;a++){const n=o[e][a]?.replaceAll(/\s/g,"");if(n){if(n===t)return{header:i,tables:g};if(!(s.length<2))for(const e of s)n===e&&(r={name:e,header:i,tables:g})}}}if(r){warn(`TrueType Collection does not contain "${t}" font, falling back to "${r.name}" font instead.`);return{header:r.header,tables:r.tables}}throw new FormatError(`TrueType Collection does not contain "${t}" font.`)}(t,this.name);r=e.header;n=e.tables}else{r=readOpenTypeHeader(t);n=readTables(t,r.numTables)}const c=!n["CFF "];if(c){if(!n.loca)throw new FormatError('Required "loca" table is not found');if(!n.glyf){warn('Required "glyf" table is not found -- trying to recover.');n.glyf={tag:"glyf",data:new Uint8Array(0)}}this.isOpenType=!1}else{const t=i.composite&&(i.cidToGidMap?.length>0||!(i.cMap instanceof IdentityCMap));if("OTTO"===r.version&&!t||!n.head||!n.hhea||!n.maxp||!n.post){o=new Stream(n["CFF "].data);g=new CFFFont(o,i);adjustWidths(i);return this.convert(e,g,i)}delete n.glyf;delete n.loca;delete n.fpgm;delete n.prep;delete n["cvt "];this.isOpenType=!0}if(!n.maxp)throw new FormatError('Required "maxp" table is not found');t.pos=(t.start||0)+n.maxp.offset;let C=t.getInt32();const h=t.getUint16();if(65536!==C&&20480!==C){if(6===n.maxp.length)C=20480;else{if(!(n.maxp.length>=32))throw new FormatError('"maxp" table has a wrong version number');C=65536}!function writeUint32(e,t,i){e[t+3]=255&i;e[t+2]=i>>>8;e[t+1]=i>>>16;e[t]=i>>>24}(n.maxp.data,0,C)}if(i.scaleFactors?.length===h&&c){const{scaleFactors:e}=i,t=int16(n.head.data[50],n.head.data[51]),a=new GlyfTable({glyfTable:n.glyf.data,isGlyphLocationsLong:t,locaTable:n.loca.data,numGlyphs:h});a.scale(e);const{glyf:s,loca:r,isLocationLong:g}=a.write();n.glyf.data=s;n.loca.data=r;if(g!==!!t){n.head.data[50]=0;n.head.data[51]=g?1:0}const o=n.hmtx.data;for(let t=0;t<h;t++){const i=4*t,a=Math.round(e[t]*int16(o[i],o[i+1]));o[i]=a>>8&255;o[i+1]=255&a;writeSignedInt16(o,i+2,Math.round(e[t]*signedInt16(o[i+2],o[i+3])))}}let l=h+1,Q=!0;if(l>65535){Q=!1;l=h;warn("Not enough space in glyfs to duplicate first glyph.")}let E=0,u=0;if(C>=65536&&n.maxp.length>=32){t.pos+=8;if(t.getUint16()>2){n.maxp.data[14]=0;n.maxp.data[15]=2}t.pos+=4;E=t.getUint16();t.pos+=4;u=t.getUint16()}n.maxp.data[4]=l>>8;n.maxp.data[5]=255&l;const d=function sanitizeTTPrograms(e,t,i,a){const s={functionsDefined:[],functionsUsed:[],functionsStackDeltas:[],tooComplexToFollowFunctions:!1,hintsValid:!0};e&&sanitizeTTProgram(e,s);t&&sanitizeTTProgram(t,s);e&&function checkInvalidFunctions(e,t){if(!e.tooComplexToFollowFunctions)if(e.functionsDefined.length>t){warn("TT: more functions defined than expected");e.hintsValid=!1}else for(let i=0,a=e.functionsUsed.length;i<a;i++){if(i>t){warn("TT: invalid function id: "+i);e.hintsValid=!1;return}if(e.functionsUsed[i]&&!e.functionsDefined[i]){warn("TT: undefined function: "+i);e.hintsValid=!1;return}}}(s,a);if(i&&1&i.length){const e=new Uint8Array(i.length+1);e.set(i.data);i.data=e}return s.hintsValid}(n.fpgm,n.prep,n["cvt "],E);if(!d){delete n.fpgm;delete n.prep;delete n["cvt "]}!function sanitizeMetrics(e,t,i,a,s,r){if(!t){i&&(i.data=null);return}e.pos=(e.start||0)+t.offset;e.pos+=4;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;const n=e.getUint16();e.pos+=8;e.pos+=2;let g=e.getUint16();if(0!==n){if(!(2&int16(a.data[44],a.data[45]))){t.data[22]=0;t.data[23]=0}}if(g>s){info(`The numOfMetrics (${g}) should not be greater than the numGlyphs (${s}).`);g=s;t.data[34]=(65280&g)>>8;t.data[35]=255&g}const o=s-g-(i.length-4*g>>1);if(o>0){const e=new Uint8Array(i.length+2*o);e.set(i.data);if(r){e[i.length]=i.data[2];e[i.length+1]=i.data[3]}i.data=e}}(t,n.hhea,n.hmtx,n.head,l,Q);if(!n.head)throw new FormatError('Required "head" table is not found');!function sanitizeHead(e,t,i){const a=e.data,s=function int32(e,t,i,a){return(e<<24)+(t<<16)+(i<<8)+a}(a[0],a[1],a[2],a[3]);if(s>>16!=1){info("Attempting to fix invalid version in head table: "+s);a[0]=0;a[1]=1;a[2]=0;a[3]=0}const r=int16(a[50],a[51]);if(r<0||r>1){info("Attempting to fix invalid indexToLocFormat in head table: "+r);const e=t+1;if(i===e<<1){a[50]=0;a[51]=0}else{if(i!==e<<2)throw new FormatError("Could not fix indexToLocFormat: "+r);a[50]=0;a[51]=1}}}(n.head,h,c?n.loca.length:0);let f=Object.create(null);if(c){const e=int16(n.head.data[50],n.head.data[51]),t=function sanitizeGlyphLocations(e,t,i,a,s,r,n){let g,o,c;if(a){g=4;o=function fontItemDecodeLong(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};c=function fontItemEncodeLong(e,t,i){e[t]=i>>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}}else{g=2;o=function fontItemDecode(e,t){return e[t]<<9|e[t+1]<<1};c=function fontItemEncode(e,t,i){e[t]=i>>9&255;e[t+1]=i>>1&255}}const C=r?i+1:i,h=g*(1+C),l=new Uint8Array(h);l.set(e.data.subarray(0,h));e.data=l;const Q=t.data,E=Q.length,u=new Uint8Array(E);let d,f;const p=[];for(d=0,f=0;d<i+1;d++,f+=g){let e=o(l,f);e>E&&(e=E);p.push({index:d,offset:e,endOffset:0})}p.sort(((e,t)=>e.offset-t.offset));for(d=0;d<i;d++)p[d].endOffset=p[d+1].offset;p.sort(((e,t)=>e.index-t.index));for(d=0;d<i;d++){const{offset:e,endOffset:t}=p[d];if(0!==e||0!==t)break;const i=p[d+1].offset;if(0!==i){p[d].endOffset=i;break}}const m=p.at(-2);0!==m.offset&&0===m.endOffset&&(m.endOffset=E);const y=Object.create(null);let w=0;c(l,0,w);for(d=0,f=g;d<i;d++,f+=g){const e=sanitizeGlyph(Q,p[d].offset,p[d].endOffset,u,w,s),t=e.length;0===t&&(y[d]=!0);e.sizeOfInstructions>n&&(n=e.sizeOfInstructions);w+=t;c(l,f,w)}if(0===w){const e=new Uint8Array([0,1,0,0,0,0,0,0,0,0,0,0,0,0,49,0]);for(d=0,f=g;d<C;d++,f+=g)c(l,f,e.length);t.data=e}else if(r){const i=o(l,g);if(u.length>i+w)t.data=u.subarray(0,i+w);else{t.data=new Uint8Array(i+w);t.data.set(u.subarray(0,w))}t.data.set(u.subarray(0,i),w);c(e.data,l.length-g,w+i)}else t.data=u.subarray(0,w);return{missingGlyphs:y,maxSizeOfInstructions:n}}(n.loca,n.glyf,h,e,d,Q,u);f=t.missingGlyphs;if(C>=65536&&n.maxp.length>=32){n.maxp.data[26]=t.maxSizeOfInstructions>>8;n.maxp.data[27]=255&t.maxSizeOfInstructions}}if(!n.hhea)throw new FormatError('Required "hhea" table is not found');if(0===n.hhea.data[10]&&0===n.hhea.data[11]){n.hhea.data[10]=255;n.hhea.data[11]=255}const p={unitsPerEm:int16(n.head.data[18],n.head.data[19]),yMax:signedInt16(n.head.data[42],n.head.data[43]),yMin:signedInt16(n.head.data[38],n.head.data[39]),ascent:signedInt16(n.hhea.data[4],n.hhea.data[5]),descent:signedInt16(n.hhea.data[6],n.hhea.data[7]),lineGap:signedInt16(n.hhea.data[8],n.hhea.data[9])};this.ascent=p.ascent/p.unitsPerEm;this.descent=p.descent/p.unitsPerEm;this.lineGap=p.lineGap/p.unitsPerEm;if(this.cssFontInfo?.lineHeight){this.lineHeight=this.cssFontInfo.metrics.lineHeight;this.lineGap=this.cssFontInfo.metrics.lineGap}else this.lineHeight=this.ascent-this.descent+this.lineGap;n.post&&function readPostScriptTable(e,i,a){const s=(t.start||0)+e.offset;t.pos=s;const r=s+e.length,n=t.getInt32();t.skip(28);let g,o,c=!0;switch(n){case 65536:g=ji;break;case 131072:const e=t.getUint16();if(e!==a){c=!1;break}const s=[];for(o=0;o<e;++o){const e=t.getUint16();if(e>=32768){c=!1;break}s.push(e)}if(!c)break;const C=[],h=[];for(;t.pos<r;){const e=t.getByte();h.length=e;for(o=0;o<e;++o)h[o]=String.fromCharCode(t.getByte());C.push(h.join(""))}g=[];for(o=0;o<e;++o){const e=s[o];e<258?g.push(ji[e]):g.push(C[e-258])}break;case 196608:break;default:warn("Unknown/unsupported post table version "+n);c=!1;i.defaultEncoding&&(g=i.defaultEncoding)}i.glyphNames=g;return c}(n.post,i,h);n.post={tag:"post",data:createPostTable(i)};const m=Object.create(null);function hasGlyph(e){return!f[e]}if(i.composite){const e=i.cidToGidMap||[],t=0===e.length;i.cMap.forEach((function(i,a){"string"==typeof a&&(a=convertCidString(i,a,!0));if(a>65535)throw new FormatError("Max size of CID is 65,535");let s=-1;t?s=a:void 0!==e[a]&&(s=e[a]);s>=0&&s<h&&hasGlyph(s)&&(m[i]=s)}))}else{const e=function readCmapTable(e,t,i,a){if(!e){warn("No cmap table available.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}let s,r=(t.start||0)+e.offset;t.pos=r;t.skip(2);const n=t.getUint16();let g,o=!1;for(let e=0;e<n;e++){const s=t.getUint16(),r=t.getUint16(),c=t.getInt32()>>>0;let C=!1;if(g?.platformId!==s||g?.encodingId!==r){if(0!==s||0!==r&&1!==r&&3!==r)if(1===s&&0===r)C=!0;else if(3!==s||1!==r||!a&&g){if(i&&3===s&&0===r){C=!0;let i=!0;if(e<n-1){const e=t.peekBytes(2);int16(e[0],e[1])<s&&(i=!1)}i&&(o=!0)}}else{C=!0;i||(o=!0)}else C=!0;C&&(g={platformId:s,encodingId:r,offset:c});if(o)break}}g&&(t.pos=r+g.offset);if(!g||-1===t.peekByte()){warn("Could not find a preferred cmap table.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}const c=t.getUint16();let C=!1;const h=[];let l,Q;if(0===c){t.skip(4);for(l=0;l<256;l++){const e=t.getByte();e&&h.push({charCode:l,glyphId:e})}C=!0}else if(2===c){t.skip(4);const e=[];let i=0;for(let a=0;a<256;a++){const a=t.getUint16()>>3;e.push(a);i=Math.max(a,i)}const a=[];for(let e=0;e<=i;e++)a.push({firstCode:t.getUint16(),entryCount:t.getUint16(),idDelta:signedInt16(t.getByte(),t.getByte()),idRangePos:t.pos+t.getUint16()});for(let i=0;i<256;i++)if(0===e[i]){t.pos=a[0].idRangePos+2*i;Q=t.getUint16();h.push({charCode:i,glyphId:Q})}else{const s=a[e[i]];for(l=0;l<s.entryCount;l++){const e=(i<<8)+l+s.firstCode;t.pos=s.idRangePos+2*l;Q=t.getUint16();0!==Q&&(Q=(Q+s.idDelta)%65536);h.push({charCode:e,glyphId:Q})}}}else if(4===c){t.skip(4);const e=t.getUint16()>>1;t.skip(6);const i=[];let a;for(a=0;a<e;a++)i.push({end:t.getUint16()});t.skip(2);for(a=0;a<e;a++)i[a].start=t.getUint16();for(a=0;a<e;a++)i[a].delta=t.getUint16();let n,g=0;for(a=0;a<e;a++){s=i[a];const r=t.getUint16();if(r){n=(r>>1)-(e-a);s.offsetIndex=n;g=Math.max(g,n+s.end-s.start+1)}else s.offsetIndex=-1}const o=[];for(l=0;l<g;l++)o.push(t.getUint16());for(a=0;a<e;a++){s=i[a];r=s.start;const e=s.end,t=s.delta;n=s.offsetIndex;for(l=r;l<=e;l++)if(65535!==l){Q=n<0?l:o[n+l-r];Q=Q+t&65535;h.push({charCode:l,glyphId:Q})}}}else if(6===c){t.skip(4);const e=t.getUint16(),i=t.getUint16();for(l=0;l<i;l++){Q=t.getUint16();const i=e+l;h.push({charCode:i,glyphId:Q})}}else{if(12!==c){warn("cmap table has unsupported format: "+c);return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}{t.skip(10);const e=t.getInt32()>>>0;for(l=0;l<e;l++){const e=t.getInt32()>>>0,i=t.getInt32()>>>0;let a=t.getInt32()>>>0;for(let t=e;t<=i;t++)h.push({charCode:t,glyphId:a++})}}}h.sort((function(e,t){return e.charCode-t.charCode}));for(let e=1;e<h.length;e++)if(h[e-1].charCode===h[e].charCode){h.splice(e,1);e--}return{platformId:g.platformId,encodingId:g.encodingId,mappings:h,hasShortCmap:C}}(n.cmap,t,this.isSymbolicFont,i.hasEncoding),a=e.platformId,s=e.encodingId,r=e.mappings;let g=[],o=!1;!i.hasEncoding||"MacRomanEncoding"!==i.baseEncodingName&&"WinAnsiEncoding"!==i.baseEncodingName||(g=getEncoding(i.baseEncodingName));if(i.hasEncoding&&!this.isSymbolicFont&&(3===a&&1===s||1===a&&0===s)){const e=Mi();for(let t=0;t<256;t++){let n;n=void 0!==this.differences[t]?this.differences[t]:g.length&&""!==g[t]?g[t]:yi[t];if(!n)continue;const o=recoverGlyphName(n,e);let c;3===a&&1===s?c=e[o]:1===a&&0===s&&(c=mi.indexOf(o));if(void 0===c){if(!i.glyphNames&&i.hasIncludedToUnicodeMap&&!(this.toUnicode instanceof IdentityToUnicodeMap)){const e=this.toUnicode.get(t);e&&(c=e.codePointAt(0))}if(void 0===c)continue}for(const e of r)if(e.charCode===c){m[t]=e.glyphId;break}}}else if(0===a){for(const e of r)m[e.charCode]=e.glyphId;o=!0}else if(3===a&&0===s)for(const e of r){let t=e.charCode;t>=61440&&t<=61695&&(t&=255);m[t]=e.glyphId}else for(const e of r)m[e.charCode]=e.glyphId;if(i.glyphNames&&(g.length||this.differences.length))for(let e=0;e<256;++e){if(!o&&void 0!==m[e])continue;const t=this.differences[e]||g[e];if(!t)continue;const a=i.glyphNames.indexOf(t);a>0&&hasGlyph(a)&&(m[e]=a)}}0===m.length&&(m[0]=0);let y=l-1;Q||(y=0);if(!i.cssFontInfo){const e=adjustMapping(m,hasGlyph,y,this.toUnicode);this.toFontChar=e.toFontChar;n.cmap={tag:"cmap",data:createCmapTable(e.charCodeToGlyphId,e.toUnicodeExtraMap,l)};n["OS/2"]&&function validateOS2Table(e,t){t.pos=(t.start||0)+e.offset;const i=t.getUint16();t.skip(60);const a=t.getUint16();if(i<4&&768&a)return!1;if(t.getUint16()>t.getUint16())return!1;t.skip(6);if(0===t.getUint16())return!1;e.data[8]=e.data[9]=0;return!0}(n["OS/2"],t)||(n["OS/2"]={tag:"OS/2",data:createOS2Table(i,e.charCodeToGlyphId,p)})}if(!c)try{o=new Stream(n["CFF "].data);g=new CFFParser(o,i,Ti).parse();g.duplicateFirstGlyph();const e=new CFFCompiler(g);n["CFF "].data=e.compile()}catch{warn("Failed to compile font "+i.loadedName)}if(n.name){const[t,a]=readNameTable(n.name);n.name.data=createNameTable(e,t);this.psName=t[0][6]||null;i.composite||function adjustTrueTypeToUnicode(e,t,i){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(e.hasEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;if(!t)return;if(0===i.length)return;if(e.defaultEncoding===wi)return;for(const e of i)if(!isWinNameRecord(e))return;const a=wi,s=[],r=Mi();for(const e in a){const t=a[e];if(""===t)continue;const i=r[t];void 0!==i&&(s[e]=String.fromCharCode(i))}s.length>0&&e.toUnicode.amend(s)}(i,this.isSymbolicFont,a)}else n.name={tag:"name",data:createNameTable(this.name)};const w=new OpenTypeFileBuilder(r.version);for(const e in n)w.addTable(e,n[e].data);return w.toArray()}convert(e,t,i){i.fixedPitch=!1;i.builtInEncoding&&function adjustType1ToUnicode(e,t){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(t===e.defaultEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const i=[],a=Mi();for(const s in t){if(e.hasEncoding&&(e.baseEncodingName||void 0!==e.differences[s]))continue;const r=getUnicodeForGlyph(t[s],a);-1!==r&&(i[s]=String.fromCharCode(r))}i.length>0&&e.toUnicode.amend(i)}(i,i.builtInEncoding);let s=1;t instanceof CFFFont&&(s=t.numGlyphs-1);const r=t.getGlyphMapping(i);let n=null,g=r,o=null;if(!i.cssFontInfo){n=adjustMapping(r,t.hasGlyphId.bind(t),s,this.toUnicode);this.toFontChar=n.toFontChar;g=n.charCodeToGlyphId;o=n.toUnicodeExtraMap}const c=t.numGlyphs;function getCharCodes(e,t){let i=null;for(const a in e)t===e[a]&&(i||=[]).push(0|a);return i}function createCharCode(e,t){for(const i in e)if(t===e[i])return 0|i;n.charCodeToGlyphId[n.nextAvailableFontCharCode]=t;return n.nextAvailableFontCharCode++}const C=t.seacs;if(n&&C?.length){const e=i.fontMatrix||a,s=t.getCharset(),g=Object.create(null);for(let t in C){t|=0;const i=C[t],a=yi[i[2]],o=yi[i[3]],c=s.indexOf(a),h=s.indexOf(o);if(c<0||h<0)continue;const l={x:i[0]*e[0]+i[1]*e[2]+e[4],y:i[0]*e[1]+i[1]*e[3]+e[5]},Q=getCharCodes(r,t);if(Q)for(const e of Q){const t=n.charCodeToGlyphId,i=createCharCode(t,c),a=createCharCode(t,h);g[e]={baseFontCharCode:i,accentFontCharCode:a,accentOffset:l}}}i.seacMap=g}const h=i.fontMatrix?1/Math.max(...i.fontMatrix.slice(0,4).map(Math.abs)):1e3,l=new OpenTypeFileBuilder("OTTO");l.addTable("CFF ",t.data);l.addTable("OS/2",createOS2Table(i,g));l.addTable("cmap",createCmapTable(g,o,c));l.addTable("head","\0\0\0\0\0\0\0\0\0\0_<õ\0\0"+safeString16(h)+"\0\0\0\0\v~'\0\0\0\0\v~'\0\0"+safeString16(i.descent)+"ÿ"+safeString16(i.ascent)+string16(i.italicAngle?2:0)+"\0\0\0\0\0\0\0");l.addTable("hhea","\0\0\0"+safeString16(i.ascent)+safeString16(i.descent)+"\0\0ÿÿ\0\0\0\0\0\0"+safeString16(i.capHeight)+safeString16(Math.tan(i.italicAngle)*i.xHeight)+"\0\0\0\0\0\0\0\0\0\0\0\0"+string16(c));l.addTable("hmtx",function fontFieldsHmtx(){const e=t.charstrings,i=t.cff?t.cff.widths:null;let a="\0\0\0\0";for(let t=1,s=c;t<s;t++){let s=0;if(e){const i=e[t-1];s="width"in i?i.width:0}else i&&(s=Math.ceil(i[t]||0));a+=string16(s)+string16(0)}return a}());l.addTable("maxp","\0\0P\0"+string16(c));l.addTable("name",createNameTable(e));l.addTable("post",createPostTable(i));return l.toArray()}get _spaceWidth(){const e=["space","minus","one","i","I"];let t;for(const i of e){if(i in this.widths){t=this.widths[i];break}const e=Mi()[i];let a=0;if(this.composite&&this.cMap.contains(e)){a=this.cMap.lookup(e);"string"==typeof a&&(a=convertCidString(e,a))}!a&&this.toUnicode&&(a=this.toUnicode.charCodeOf(e));a<=0&&(a=e);t=this.widths[a];if(t)break}return shadow(this,"_spaceWidth",t||this.defaultWidth)}_charToGlyph(e,t=!1){let i,a,s,r=this._glyphCache[e];if(r?.isSpace===t)return r;let n=e;if(this.cMap?.contains(e)){n=this.cMap.lookup(e);"string"==typeof n&&(n=convertCidString(e,n))}a=this.widths[n];"number"!=typeof a&&(a=this.defaultWidth);const g=this.vmetrics?.[n];let o=this.toUnicode.get(e)||e;"number"==typeof o&&(o=String.fromCharCode(o));let c=void 0!==this.toFontChar[e];i=this.toFontChar[e]||e;if(this.missingFile){const t=this.differences[e]||this.defaultEncoding[e];if((".notdef"===t||""===t)&&"Type1"===this.type){i=32;if(""===t){a||=this._spaceWidth;o=String.fromCharCode(i)}}i=function mapSpecialUnicodeValues(e){return e>=65520&&e<=65535?0:e>=62976&&e<=63743?Ji()[e]||e:173===e?45:e}(i)}this.isType3Font&&(s=i);let C=null;if(this.seacMap?.[e]){c=!0;const t=this.seacMap[e];i=t.baseFontCharCode;C={fontChar:String.fromCodePoint(t.accentFontCharCode),offset:t.accentOffset}}let h="";"number"==typeof i&&(i<=1114111?h=String.fromCodePoint(i):warn(`charToGlyph - invalid fontCharCode: ${i}`));if(this.missingFile&&this.vertical&&1===h.length){const e=Xi()[h.charCodeAt(0)];e&&(h=o=String.fromCharCode(e))}r=new fonts_Glyph(e,h,o,C,a,g,s,t,c);return this._glyphCache[e]=r}charsToGlyphs(e){let t=this._charsCache[e];if(t)return t;t=[];if(this.cMap){const i=Object.create(null),a=e.length;let s=0;for(;s<a;){this.cMap.readCharCode(e,s,i);const{charcode:a,length:r}=i;s+=r;const n=this._charToGlyph(a,1===r&&32===e.charCodeAt(s-1));t.push(n)}}else for(let i=0,a=e.length;i<a;++i){const a=e.charCodeAt(i),s=this._charToGlyph(a,32===a);t.push(s)}return this._charsCache[e]=t}getCharPositions(e){const t=[];if(this.cMap){const i=Object.create(null);let a=0;for(;a<e.length;){this.cMap.readCharCode(e,a,i);const s=i.length;t.push([a,a+s]);a+=s}}else for(let i=0,a=e.length;i<a;++i)t.push([i,i+1]);return t}get glyphCacheValues(){return Object.values(this._glyphCache)}encodeString(e){const t=[],i=[],hasCurrentBufErrors=()=>t.length%2==1,a=this.toUnicode instanceof IdentityToUnicodeMap?e=>this.toUnicode.charCodeOf(e):e=>this.toUnicode.charCodeOf(String.fromCodePoint(e));for(let s=0,r=e.length;s<r;s++){const r=e.codePointAt(s);r>55295&&(r<57344||r>65533)&&s++;if(this.toUnicode){const e=a(r);if(-1!==e){if(hasCurrentBufErrors()){t.push(i.join(""));i.length=0}for(let t=(this.cMap?this.cMap.getCharCodeLength(e):1)-1;t>=0;t--)i.push(String.fromCharCode(e>>8*t&255));continue}}if(!hasCurrentBufErrors()){t.push(i.join(""));i.length=0}i.push(String.fromCodePoint(r))}t.push(i.join(""));return t}}class ErrorFont{constructor(e){this.error=e;this.loadedName="g_font_error";this.missingFile=!0}charsToGlyphs(){return[]}encodeString(e){return[e]}exportData(e=!1){return{error:this.error}}}const pa=2,ma=3,ya=4,wa=5,Da=6,ba=7;class Pattern{constructor(){unreachable("Cannot initialize Pattern.")}static parseShading(e,t,i,a,s){const r=e instanceof BaseStream?e.dict:e,n=r.get("ShadingType");try{switch(n){case pa:case ma:return new RadialAxialShading(r,t,i,a,s);case ya:case wa:case Da:case ba:return new MeshShading(e,t,i,a,s);default:throw new FormatError("Unsupported ShadingType: "+n)}}catch(e){if(e instanceof MissingDataException)throw e;warn(e);return new DummyShading}}}class BaseShading{static SMALL_NUMBER=1e-6;getIR(){unreachable("Abstract method `getIR` called.")}}class RadialAxialShading extends BaseShading{constructor(e,t,i,a,s){super();this.shadingType=e.get("ShadingType");let r=0;this.shadingType===pa?r=4:this.shadingType===ma&&(r=6);this.coordsArr=e.getArray("Coords");if(!isNumberArray(this.coordsArr,r))throw new FormatError("RadialAxialShading: Invalid /Coords array.");const n=ColorSpace.parse({cs:e.getRaw("CS")||e.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.bbox=lookupNormalRect(e.getArray("BBox"),null);let g=0,o=1;const c=e.getArray("Domain");isNumberArray(c,2)&&([g,o]=c);let C=!1,h=!1;const l=e.getArray("Extend");(function isBooleanArray(e,t){return Array.isArray(e)&&(null===t||e.length===t)&&e.every((e=>"boolean"==typeof e))})(l,2)&&([C,h]=l);if(!(this.shadingType!==ma||C&&h)){const[e,t,i,a,s,r]=this.coordsArr,n=Math.hypot(e-a,t-s);i<=r+n&&r<=i+n&&warn("Unsupported radial gradient.")}this.extendStart=C;this.extendEnd=h;const Q=e.getRaw("Function"),E=a.createFromArray(Q),u=(o-g)/840,d=this.colorStops=[];if(g>=o||u<=0){info("Bad shading domain.");return}const f=new Float32Array(n.numComps),p=new Float32Array(1);let m,y=0;p[0]=g;E(p,0,f,0);let w=n.getRgb(f,0);const D=Util.makeHexColor(w[0],w[1],w[2]);d.push([0,D]);let b=1;p[0]=g+u;E(p,0,f,0);let F=n.getRgb(f,0),S=F[0]-w[0]+1,k=F[1]-w[1]+1,R=F[2]-w[2]+1,N=F[0]-w[0]-1,G=F[1]-w[1]-1,x=F[2]-w[2]-1;for(let e=2;e<840;e++){p[0]=g+e*u;E(p,0,f,0);m=n.getRgb(f,0);const t=e-y;S=Math.min(S,(m[0]-w[0]+1)/t);k=Math.min(k,(m[1]-w[1]+1)/t);R=Math.min(R,(m[2]-w[2]+1)/t);N=Math.max(N,(m[0]-w[0]-1)/t);G=Math.max(G,(m[1]-w[1]-1)/t);x=Math.max(x,(m[2]-w[2]-1)/t);if(!(N<=S&&G<=k&&x<=R)){const e=Util.makeHexColor(F[0],F[1],F[2]);d.push([b/840,e]);S=m[0]-F[0]+1;k=m[1]-F[1]+1;R=m[2]-F[2]+1;N=m[0]-F[0]-1;G=m[1]-F[1]-1;x=m[2]-F[2]-1;y=b;w=F}b=e;F=m}const U=Util.makeHexColor(F[0],F[1],F[2]);d.push([1,U]);let M="transparent";if(e.has("Background")){m=n.getRgb(e.get("Background"),0);M=Util.makeHexColor(m[0],m[1],m[2])}if(!C){d.unshift([0,M]);d[1][0]+=BaseShading.SMALL_NUMBER}if(!h){d.at(-1)[0]-=BaseShading.SMALL_NUMBER;d.push([1,M])}this.colorStops=d}getIR(){const{coordsArr:e,shadingType:t}=this;let i,a,s,r,n;if(t===pa){a=[e[0],e[1]];s=[e[2],e[3]];r=null;n=null;i="axial"}else if(t===ma){a=[e[0],e[1]];s=[e[3],e[4]];r=e[2];n=e[5];i="radial"}else unreachable(`getPattern type unknown: ${t}`);return["RadialAxial",i,this.bbox,this.colorStops,a,s,r,n]}}class MeshStreamReader{constructor(e,t){this.stream=e;this.context=t;this.buffer=0;this.bufferLength=0;const i=t.numComps;this.tmpCompsBuf=new Float32Array(i);const a=t.colorSpace.numComps;this.tmpCsCompsBuf=t.colorFn?new Float32Array(a):this.tmpCompsBuf}get hasData(){if(this.stream.end)return this.stream.pos<this.stream.end;if(this.bufferLength>0)return!0;const e=this.stream.getByte();if(e<0)return!1;this.buffer=e;this.bufferLength=8;return!0}readBits(e){let t=this.buffer,i=this.bufferLength;if(32===e){if(0===i)return(this.stream.getByte()<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte())>>>0;t=t<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte();const e=this.stream.getByte();this.buffer=e&(1<<i)-1;return(t<<8-i|(255&e)>>i)>>>0}if(8===e&&0===i)return this.stream.getByte();for(;i<e;){t=t<<8|this.stream.getByte();i+=8}i-=e;this.bufferLength=i;this.buffer=t&(1<<i)-1;return t>>i}align(){this.buffer=0;this.bufferLength=0}readFlag(){return this.readBits(this.context.bitsPerFlag)}readCoordinate(){const e=this.context.bitsPerCoordinate,t=this.readBits(e),i=this.readBits(e),a=this.context.decode,s=e<32?1/((1<<e)-1):2.3283064365386963e-10;return[t*s*(a[1]-a[0])+a[0],i*s*(a[3]-a[2])+a[2]]}readComponents(){const e=this.context.numComps,t=this.context.bitsPerComponent,i=t<32?1/((1<<t)-1):2.3283064365386963e-10,a=this.context.decode,s=this.tmpCompsBuf;for(let r=0,n=4;r<e;r++,n+=2){const e=this.readBits(t);s[r]=e*i*(a[n+1]-a[n])+a[n]}const r=this.tmpCsCompsBuf;this.context.colorFn&&this.context.colorFn(s,0,r,0);return this.context.colorSpace.getRgb(r,0)}}let Fa=Object.create(null);function getB(e){return Fa[e]||=function buildB(e){const t=[];for(let i=0;i<=e;i++){const a=i/e,s=1-a;t.push(new Float32Array([s**3,3*a*s**2,3*a**2*s,a**3]))}return t}(e)}class MeshShading extends BaseShading{static MIN_SPLIT_PATCH_CHUNKS_AMOUNT=3;static MAX_SPLIT_PATCH_CHUNKS_AMOUNT=20;static TRIANGLE_DENSITY=20;constructor(e,t,i,a,s){super();if(!(e instanceof BaseStream))throw new FormatError("Mesh data is not a stream");const r=e.dict;this.shadingType=r.get("ShadingType");this.bbox=lookupNormalRect(r.getArray("BBox"),null);const n=ColorSpace.parse({cs:r.getRaw("CS")||r.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.background=r.has("Background")?n.getRgb(r.get("Background"),0):null;const g=r.getRaw("Function"),o=g?a.createFromArray(g):null;this.coords=[];this.colors=[];this.figures=[];const c={bitsPerCoordinate:r.get("BitsPerCoordinate"),bitsPerComponent:r.get("BitsPerComponent"),bitsPerFlag:r.get("BitsPerFlag"),decode:r.getArray("Decode"),colorFn:o,colorSpace:n,numComps:o?1:n.numComps},C=new MeshStreamReader(e,c);let h=!1;switch(this.shadingType){case ya:this._decodeType4Shading(C);break;case wa:const e=0|r.get("VerticesPerRow");if(e<2)throw new FormatError("Invalid VerticesPerRow");this._decodeType5Shading(C,e);break;case Da:this._decodeType6Shading(C);h=!0;break;case ba:this._decodeType7Shading(C);h=!0;break;default:unreachable("Unsupported mesh type.")}if(h){this._updateBounds();for(let e=0,t=this.figures.length;e<t;e++)this._buildFigureFromPatch(e)}this._updateBounds();this._packData()}_decodeType4Shading(e){const t=this.coords,i=this.colors,a=[],s=[];let r=0;for(;e.hasData;){const n=e.readFlag(),g=e.readCoordinate(),o=e.readComponents();if(0===r){if(!(0<=n&&n<=2))throw new FormatError("Unknown type4 flag");switch(n){case 0:r=3;break;case 1:s.push(s.at(-2),s.at(-1));r=1;break;case 2:s.push(s.at(-3),s.at(-1));r=1}a.push(n)}s.push(t.length);t.push(g);i.push(o);r--;e.align()}this.figures.push({type:"triangles",coords:new Int32Array(s),colors:new Int32Array(s)})}_decodeType5Shading(e,t){const i=this.coords,a=this.colors,s=[];for(;e.hasData;){const t=e.readCoordinate(),r=e.readComponents();s.push(i.length);i.push(t);a.push(r)}this.figures.push({type:"lattice",coords:new Int32Array(s),colors:new Int32Array(s),verticesPerRow:t})}_decodeType6Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type6 flag");const n=t.length;for(let i=0,a=0!==r?8:12;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[11]=n+7;a[4]=n+1;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[11]=n+3;a[4]=a[2];a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}a[5]=t.length;t.push([(-4*t[a[0]][0]-t[a[15]][0]+6*(t[a[4]][0]+t[a[1]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[13]][0]+t[a[7]][0]))/9,(-4*t[a[0]][1]-t[a[15]][1]+6*(t[a[4]][1]+t[a[1]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[13]][1]+t[a[7]][1]))/9]);a[6]=t.length;t.push([(-4*t[a[3]][0]-t[a[12]][0]+6*(t[a[2]][0]+t[a[7]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[4]][0]+t[a[14]][0]))/9,(-4*t[a[3]][1]-t[a[12]][1]+6*(t[a[2]][1]+t[a[7]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[4]][1]+t[a[14]][1]))/9]);a[9]=t.length;t.push([(-4*t[a[12]][0]-t[a[3]][0]+6*(t[a[8]][0]+t[a[13]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[11]][0]+t[a[1]][0]))/9,(-4*t[a[12]][1]-t[a[3]][1]+6*(t[a[8]][1]+t[a[13]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[11]][1]+t[a[1]][1]))/9]);a[10]=t.length;t.push([(-4*t[a[15]][0]-t[a[0]][0]+6*(t[a[11]][0]+t[a[14]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[2]][0]+t[a[8]][0]))/9,(-4*t[a[15]][1]-t[a[0]][1]+6*(t[a[11]][1]+t[a[14]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[2]][1]+t[a[8]][1]))/9]);this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_decodeType7Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type7 flag");const n=t.length;for(let i=0,a=0!==r?12:16;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[9]=n+13;a[10]=n+14;a[11]=n+7;a[4]=n+1;a[5]=n+12;a[6]=n+15;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=a[2];a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_buildFigureFromPatch(e){const t=this.figures[e];assert("patch"===t.type,"Unexpected patch mesh figure");const i=this.coords,a=this.colors,s=t.coords,r=t.colors,n=Math.min(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),g=Math.min(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]),o=Math.max(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),c=Math.max(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]);let C=Math.ceil((o-n)*MeshShading.TRIANGLE_DENSITY/(this.bounds[2]-this.bounds[0]));C=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,C));let h=Math.ceil((c-g)*MeshShading.TRIANGLE_DENSITY/(this.bounds[3]-this.bounds[1]));h=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,h));const l=C+1,Q=new Int32Array((h+1)*l),E=new Int32Array((h+1)*l);let u=0;const d=new Uint8Array(3),f=new Uint8Array(3),p=a[r[0]],m=a[r[1]],y=a[r[2]],w=a[r[3]],D=getB(h),b=getB(C);for(let e=0;e<=h;e++){d[0]=(p[0]*(h-e)+y[0]*e)/h|0;d[1]=(p[1]*(h-e)+y[1]*e)/h|0;d[2]=(p[2]*(h-e)+y[2]*e)/h|0;f[0]=(m[0]*(h-e)+w[0]*e)/h|0;f[1]=(m[1]*(h-e)+w[1]*e)/h|0;f[2]=(m[2]*(h-e)+w[2]*e)/h|0;for(let t=0;t<=C;t++,u++){if(!(0!==e&&e!==h||0!==t&&t!==C))continue;let r=0,n=0,g=0;for(let a=0;a<=3;a++)for(let o=0;o<=3;o++,g++){const c=D[e][a]*b[t][o];r+=i[s[g]][0]*c;n+=i[s[g]][1]*c}Q[u]=i.length;i.push([r,n]);E[u]=a.length;const o=new Uint8Array(3);o[0]=(d[0]*(C-t)+f[0]*t)/C|0;o[1]=(d[1]*(C-t)+f[1]*t)/C|0;o[2]=(d[2]*(C-t)+f[2]*t)/C|0;a.push(o)}}Q[0]=s[0];E[0]=r[0];Q[C]=s[3];E[C]=r[1];Q[l*h]=s[12];E[l*h]=r[2];Q[l*h+C]=s[15];E[l*h+C]=r[3];this.figures[e]={type:"lattice",coords:Q,colors:E,verticesPerRow:l}}_updateBounds(){let e=this.coords[0][0],t=this.coords[0][1],i=e,a=t;for(let s=1,r=this.coords.length;s<r;s++){const r=this.coords[s][0],n=this.coords[s][1];e=e>r?r:e;t=t>n?n:t;i=i<r?r:i;a=a<n?n:a}this.bounds=[e,t,i,a]}_packData(){let e,t,i,a;const s=this.coords,r=new Float32Array(2*s.length);for(e=0,i=0,t=s.length;e<t;e++){const t=s[e];r[i++]=t[0];r[i++]=t[1]}this.coords=r;const n=this.colors,g=new Uint8Array(3*n.length);for(e=0,i=0,t=n.length;e<t;e++){const t=n[e];g[i++]=t[0];g[i++]=t[1];g[i++]=t[2]}this.colors=g;const o=this.figures;for(e=0,t=o.length;e<t;e++){const t=o[e],s=t.coords,r=t.colors;for(i=0,a=s.length;i<a;i++){s[i]*=2;r[i]*=3}}}getIR(){const{bounds:e}=this;if(e[2]-e[0]==0||e[3]-e[1]==0)throw new FormatError(`Invalid MeshShading bounds: [${e}].`);return["Mesh",this.shadingType,this.coords,this.colors,this.figures,e,this.bbox,this.background]}}class DummyShading extends BaseShading{getIR(){return["Dummy"]}}function getTilingPatternIR(e,t,a){const s=lookupMatrix(t.getArray("Matrix"),i),r=lookupNormalRect(t.getArray("BBox"),null);if(!r||r[2]-r[0]==0||r[3]-r[1]==0)throw new FormatError("Invalid getTilingPatternIR /BBox array.");const n=t.get("XStep");if("number"!=typeof n)throw new FormatError("Invalid getTilingPatternIR /XStep value.");const g=t.get("YStep");if("number"!=typeof g)throw new FormatError("Invalid getTilingPatternIR /YStep value.");const o=t.get("PaintType");if(!Number.isInteger(o))throw new FormatError("Invalid getTilingPatternIR /PaintType value.");const c=t.get("TilingType");if(!Number.isInteger(c))throw new FormatError("Invalid getTilingPatternIR /TilingType value.");return["TilingPattern",a,e,s,r,n,g,o,c]}const Sa=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.54657,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.73293,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.9121,.86943,.79795,.88198,.77958,.70864,.81055,.90399,.88653,.96017,.82577,.77892,.78257,.97507,1.54657,.97507,.85284,.89552,.90176,.88762,.8785,.75241,.8785,.90518,.95015,.77618,.8785,.88401,.91916,.86304,.88401,.91488,.8785,.8801,.8785,.8785,.91343,.7173,1.04106,.8785,.85075,.95794,.82616,.85162,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.12401,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.73293,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.9121,.86943,.86943,.86943,.86943,.86943,.85284,.87508,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.88762,.88762,.88762,.88762,.88762,.88762,.8715,.75241,.90518,.90518,.90518,.90518,.88401,.88401,.88401,.88401,.8785,.8785,.8801,.8801,.8801,.8801,.8801,.90747,.89049,.8785,.8785,.8785,.8785,.85162,.8785,.85162,.83908,.88762,.83908,.88762,.83908,.88762,.73293,.75241,.73293,.75241,.73293,.75241,.73293,.75241,.87289,.83016,.88506,.93125,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.81921,.77618,.81921,.77618,.81921,.77618,1,1,.87356,.8785,.91075,.89608,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76229,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.79468,.91926,.88175,.70823,.94903,.9121,.8785,1,1,.9121,.8785,.87802,.88656,.8785,.86943,.8801,.86943,.8801,.86943,.8801,.87402,.89291,.77958,.91343,1,1,.77958,.91343,.70864,.7173,.70864,.7173,.70864,.7173,.70864,.7173,1,1,.81055,.75841,.81055,1.06452,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.96017,.95794,.77892,.85162,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.88762,.77539,.8715,.87508,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70674,.98387,.94721,1.33431,1.45894,.95161,1.06303,.83908,.80352,.57184,.6965,.56289,.82001,.56029,.81235,1.02988,.83908,.7762,.68156,.80367,.73133,.78257,.87356,.86943,.95958,.75727,.89019,1.04924,.9121,.7648,.86943,.87356,.79795,.78275,.81055,.77892,.9762,.82577,.99819,.84896,.95958,.77892,.96108,1.01407,.89049,1.02988,.94211,.96108,.8936,.84021,.87842,.96399,.79109,.89049,1.00813,1.02988,.86077,.87445,.92099,.84723,.86513,.8801,.75638,.85714,.78216,.79586,.87965,.94211,.97747,.78287,.97926,.84971,1.02988,.94211,.8801,.94211,.84971,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90264,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.90518,1,1,1,1,1,1,1,1,1,1,1,1,.90548,1,1,1,1,1,1,.96017,.95794,.96017,.95794,.96017,.95794,.77892,.85162,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.92794,.87012,.87012,.87012,.89552,.89552,1.42259,.71143,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.93835,.83406,.91133,.84107,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90527,1.81055,.90527,1.81055,1.31006,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ka={lineHeight:1.2207,lineGap:.2207},Ra=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.56239,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.71805,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.90872,.85938,.79795,.87068,.77958,.69766,.81055,.90399,.88653,.96068,.82577,.77892,.78257,.97507,1.529,.97507,.85284,.89552,.90176,.94908,.86411,.74012,.86411,.88323,.95015,.86411,.86331,.88401,.91916,.86304,.88401,.9039,.86331,.86331,.86411,.86411,.90464,.70852,1.04106,.86331,.84372,.95794,.82616,.84548,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.19129,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.71805,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.90872,.85938,.85938,.85938,.85938,.85938,.85284,.87068,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.94908,.94908,.94908,.94908,.94908,.94908,.85887,.74012,.88323,.88323,.88323,.88323,.88401,.88401,.88401,.88401,.8785,.86331,.86331,.86331,.86331,.86331,.86331,.90747,.89049,.86331,.86331,.86331,.86331,.84548,.86411,.84548,.83908,.94908,.83908,.94908,.83908,.94908,.71805,.74012,.71805,.74012,.71805,.74012,.71805,.74012,.87289,.79538,.88506,.92726,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.81921,.86411,.81921,.86411,.81921,.86411,1,1,.87356,.86331,.91075,.8777,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76467,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.77312,.91926,.88175,.70823,.94903,.90872,.86331,1,1,.90872,.86331,.86906,.88116,.86331,.85938,.86331,.85938,.86331,.85938,.86331,.87402,.86549,.77958,.90464,1,1,.77958,.90464,.69766,.70852,.69766,.70852,.69766,.70852,.69766,.70852,1,1,.81055,.75841,.81055,1.06452,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.96068,.95794,.77892,.84548,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.94908,.77539,.85887,.87068,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70088,.98387,.94721,1.33431,1.45894,.95161,1.48387,.83908,.80352,.57118,.6965,.56347,.79179,.55853,.80346,1.02988,.83908,.7762,.67174,.86036,.73133,.78257,.87356,.86441,.95958,.75727,.89019,1.04924,.90872,.74889,.85938,.87891,.79795,.7957,.81055,.77892,.97447,.82577,.97466,.87179,.95958,.77892,.94252,.95612,.8753,1.02988,.92733,.94252,.87411,.84021,.8728,.95612,.74081,.8753,1.02189,1.02988,.84814,.87445,.91822,.84723,.85668,.86331,.81344,.87581,.76422,.82046,.96057,.92733,.99375,.78022,.95452,.86015,1.02988,.92733,.86331,.92733,.86015,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90631,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.88323,1,1,1,1,1,1,1,1,1,1,1,1,.85174,1,1,1,1,1,1,.96068,.95794,.96068,.95794,.96068,.95794,.77892,.84548,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.89807,.87012,.87012,.87012,.89552,.89552,1.42259,.71094,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.92972,.83406,.91133,.83326,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90616,1.81055,.90527,1.81055,1.3107,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Na={lineHeight:1.2207,lineGap:.2207},Ga=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39543,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.72346,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89249,.84118,.77452,.85374,.75186,.67789,.79776,.88844,.85066,.94309,.77818,.7306,.76659,1.10369,1.38313,1.10369,1.06139,.89552,.8739,.9245,.9245,.83203,.9245,.85865,1.09842,.9245,.9245,1.03297,1.07692,.90918,1.03297,.94959,.9245,.92274,.9245,.9245,1.02933,.77832,1.20562,.9245,.8916,.98986,.86621,.89453,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.16359,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.72346,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89249,.84118,.84118,.84118,.84118,.84118,.85284,.84557,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.9245,.9245,.9245,.9245,.9245,.9245,.84843,.83203,.85865,.85865,.85865,.85865,.82601,.82601,.82601,.82601,.94469,.9245,.92274,.92274,.92274,.92274,.92274,.90747,.86651,.9245,.9245,.9245,.9245,.89453,.9245,.89453,.8675,.9245,.8675,.9245,.8675,.9245,.72346,.83203,.72346,.83203,.72346,.83203,.72346,.83203,.85193,.8875,.86477,.99034,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.81105,.9245,.81105,.9245,.81105,.9245,1,1,.86275,.9245,.90872,.93591,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77896,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.9375,.98156,.93407,.77261,1.11429,.89249,.9245,1,1,.89249,.9245,.92534,.86698,.9245,.84118,.92274,.84118,.92274,.84118,.92274,.8667,.86291,.75186,1.02933,1,1,.75186,1.02933,.67789,.77832,.67789,.77832,.67789,.77832,.67789,.77832,1,1,.79776,.97655,.79776,1.23023,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.94309,.98986,.7306,.89453,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.9245,.76318,.84843,.84557,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67009,.96334,.93695,1.35191,1.40909,.95161,1.48387,.8675,.90861,.6192,.7363,.64824,.82411,.56321,.85696,1.23516,.8675,.81552,.7286,.84134,.73206,.76659,.86275,.84369,.90685,.77892,.85871,1.02638,.89249,.75828,.84118,.85984,.77452,.76466,.79776,.7306,.90782,.77818,.903,.87291,.90685,.7306,.99058,1.03667,.94635,1.23516,.9849,.99058,.92393,.8916,.942,1.03667,.75026,.94635,1.0297,1.23516,.90918,.94048,.98217,.89746,.84153,.92274,.82507,.88832,.84438,.88178,1.03525,.9849,1.00225,.78086,.97248,.89404,1.23516,.9849,.92274,.9849,.89404,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.89693,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.85865,1,1,1,1,1,1,1,1,1,1,1,1,.90933,1,1,1,1,1,1,.94309,.98986,.94309,.98986,.94309,.98986,.7306,.89453,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.68994,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.97858,.82616,.91133,.83437,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90572,1.81055,.90749,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85284,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],xa={lineHeight:1.2207,lineGap:.2207},Ua=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39016,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.73834,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89385,.85122,.77452,.86503,.75186,.68887,.79776,.88844,.85066,.94258,.77818,.7306,.76659,1.10369,1.39016,1.10369,1.06139,.89552,.8739,.86128,.94469,.8457,.94469,.89464,1.09842,.84636,.94469,1.03297,1.07692,.90918,1.03297,.95897,.94469,.9482,.94469,.94469,1.04692,.78223,1.20562,.94469,.90332,.98986,.86621,.90527,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.08707,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.73834,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89385,.85122,.85122,.85122,.85122,.85122,.85284,.85311,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.86128,.86128,.86128,.86128,.86128,.86128,.8693,.8457,.89464,.89464,.89464,.89464,.82601,.82601,.82601,.82601,.94469,.94469,.9482,.9482,.9482,.9482,.9482,.90747,.86651,.94469,.94469,.94469,.94469,.90527,.94469,.90527,.8675,.86128,.8675,.86128,.8675,.86128,.73834,.8457,.73834,.8457,.73834,.8457,.73834,.8457,.85193,.92454,.86477,.9921,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.81105,.84636,.81105,.84636,.81105,.84636,1,1,.86275,.94469,.90872,.95786,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77741,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.90452,.98156,1.11842,.77261,1.11429,.89385,.94469,1,1,.89385,.94469,.95877,.86901,.94469,.85122,.9482,.85122,.9482,.85122,.9482,.8667,.90016,.75186,1.04692,1,1,.75186,1.04692,.68887,.78223,.68887,.78223,.68887,.78223,.68887,.78223,1,1,.79776,.92188,.79776,1.23023,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.94258,.98986,.7306,.90527,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.86128,.76318,.8693,.85311,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67742,.96334,.93695,1.35191,1.40909,.95161,1.48387,.86686,.90861,.62267,.74359,.65649,.85498,.56963,.88254,1.23516,.8675,.81552,.75443,.84503,.73206,.76659,.86275,.85122,.90685,.77892,.85746,1.02638,.89385,.75657,.85122,.86275,.77452,.74171,.79776,.7306,.95165,.77818,.89772,.88831,.90685,.7306,.98142,1.02191,.96576,1.23516,.99018,.98142,.9236,.89258,.94035,1.02191,.78848,.96576,.9561,1.23516,.90918,.92578,.95424,.89746,.83969,.9482,.80113,.89442,.85208,.86155,.98022,.99018,1.00452,.81209,.99247,.89181,1.23516,.99018,.9482,.99018,.89181,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.88844,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89464,1,1,1,1,1,1,1,1,1,1,1,1,.96766,1,1,1,1,1,1,.94258,.98986,.94258,.98986,.94258,.98986,.7306,.90527,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.69043,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.99331,.82616,.91133,.84286,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90527,1.81055,.90527,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1.07185,.99413,.96334,1.08065,1,1,1,1,1,1,1,1,1,1,1],Ma={lineHeight:1.2207,lineGap:.2207},La=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.03374,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.00042,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.03828,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00034,.99977,1,.99997,1.00026,1.00078,1.00036,.99973,1.00013,1.0006,.99977,.99977,.99988,.85148,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,1.00069,1.00022,.99977,1.00001,.99984,1.00026,1.00001,1.00024,1.00001,.9999,1,1.0006,1.00001,1.00041,.99962,1.00026,1.0006,.99995,1.00041,.99942,.99973,.99927,1.00082,.99902,1.00026,1.00087,1.0006,1.00069,.99973,.99867,.99973,.9993,1.00026,1.00049,1.00056,1,.99988,.99935,.99995,.99954,1.00055,.99945,1.00032,1.0006,.99995,1.00026,.99995,1.00032,1.00001,1.00008,.99971,1.00019,.9994,1.00001,1.0006,1.00044,.99973,1.00023,1.00047,1,.99942,.99561,.99989,1.00035,.99977,1.00035,.99977,1.00019,.99944,1.00001,1.00021,.99926,1.00035,1.00035,.99942,1.00048,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.99989,1.00057,1.00001,.99936,1.00052,1.00012,.99996,1.00043,1,1.00035,.9994,.99976,1.00035,.99973,1.00052,1.00041,1.00119,1.00037,.99973,1.00002,.99986,1.00041,1.00041,.99902,.9996,1.00034,.99999,1.00026,.99999,1.00026,.99973,1.00052,.99973,1,.99973,1.00041,1.00075,.9994,1.0003,.99999,1,1.00041,.99955,1,.99915,.99973,.99973,1.00026,1.00119,.99955,.99973,1.0006,.99911,1.0006,1.00026,.99972,1.00026,.99902,1.00041,.99973,.99999,1,1,1.00038,1.0005,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,1.00047,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Ha={lineHeight:1.2,lineGap:.2},Ja=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.0044,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99971,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.01011,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99977,1,1,1.00026,.99969,.99972,.99981,.9998,1.0006,.99977,.99977,1.00022,.91155,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,.99966,1.00022,1.00032,1.00001,.99944,1.00026,1.00001,.99968,1.00001,1.00047,1,1.0006,1.00001,.99981,1.00101,1.00026,1.0006,.99948,.99981,1.00064,.99973,.99942,1.00101,1.00061,1.00026,1.00069,1.0006,1.00014,.99973,1.01322,.99973,1.00065,1.00026,1.00012,.99923,1,1.00064,1.00076,.99948,1.00055,1.00063,1.00007,.99943,1.0006,.99948,1.00026,.99948,.99943,1.00001,1.00001,1.00029,1.00038,1.00035,1.00001,1.0006,1.0006,.99973,.99978,1.00001,1.00057,.99989,.99967,.99964,.99967,.99977,.99999,.99977,1.00038,.99977,1.00001,.99973,1.00066,.99967,.99967,1.00041,.99998,.99999,.99977,1.00022,.99967,1.00001,.99977,1.00026,.99964,1.00031,1.00001,.99999,.99999,1,1.00023,1,1,.99999,1.00035,1.00001,.99999,.99973,.99977,.99999,1.00058,.99973,.99973,.99955,.9995,1.00026,1.00026,1.00032,.99989,1.00034,.99999,1.00026,1.00026,1.00026,.99973,.45998,.99973,1.00026,.99973,1.00001,.99999,.99982,.99994,.99996,1,1.00042,1.00044,1.00029,1.00023,.99973,.99973,1.00026,.99949,1.00002,.99973,1.0006,1.0006,1.0006,.99975,1.00026,1.00026,1.00032,.98685,.99973,1.00026,1,1,.99966,1.00044,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1,.99973,.99971,.99978,1,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00098,1,1,1,1.00049,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Ya={lineHeight:1.35,lineGap:.2},va=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.0288,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,.99946,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.06311,.99973,1.00024,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,1.00041,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.89547,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00001,1,1.00054,.99977,1.00084,1.00007,.99973,1.00013,.99924,1.00001,1.00001,.99945,.91221,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00001,.99999,.99977,.99933,1.00022,1.00054,1.00001,1.00065,1.00026,1.00001,1.0001,1.00001,1.00052,1,1.0006,1.00001,.99945,.99897,.99968,.99924,1.00036,.99945,.99949,1,1.0006,.99897,.99918,.99968,.99911,.99924,1,.99962,1.01487,1,1.0005,.99973,1.00012,1.00043,1,.99995,.99994,1.00036,.99947,1.00019,1.00063,1.00025,.99924,1.00036,.99973,1.00036,1.00025,1.00001,1.00001,1.00027,1.0001,1.00068,1.00001,1.0006,1.0006,1,1.00008,.99957,.99972,.9994,.99954,.99975,1.00051,1.00001,1.00019,1.00001,1.0001,.99986,1.00001,1.00001,1.00038,.99954,.99954,.9994,1.00066,.99999,.99977,1.00022,1.00054,1.00001,.99977,1.00026,.99975,1.0001,1.00001,.99993,.9995,.99955,1.00016,.99978,.99974,1.00019,1.00022,.99955,1.00053,.99973,1.00089,1.00005,.99967,1.00048,.99973,1.00002,1.00034,.99973,.99973,.99964,1.00006,1.00066,.99947,.99973,.98894,.99973,1,.44898,1,.99946,1,1.00039,1.00082,.99991,.99991,.99985,1.00022,1.00023,1.00061,1.00006,.99966,.99973,.99973,.99973,1.00019,1.0008,1,.99924,.99924,.99924,.99983,1.00044,.99973,.99964,.98332,1,.99973,1,1,.99962,.99895,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,1.00423,.99925,.99999,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00049,1,1.00245,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,1.00003,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Ka={lineHeight:1.35,lineGap:.2},Ta=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.04596,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,1.00019,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.02572,.99973,1.00005,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,.99999,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.84533,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99928,1,.99977,1.00013,1.00055,.99947,.99945,.99941,.99924,1.00001,1.00001,1.0004,.91621,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00005,.99999,.99977,1.00015,1.00022,.99977,1.00001,.99973,1.00026,1.00001,1.00019,1.00001,.99946,1,1.0006,1.00001,.99978,1.00045,.99973,.99924,1.00023,.99978,.99966,1,1.00065,1.00045,1.00019,.99973,.99973,.99924,1,1,.96499,1,1.00055,.99973,1.00008,1.00027,1,.9997,.99995,1.00023,.99933,1.00019,1.00015,1.00031,.99924,1.00023,.99973,1.00023,1.00031,1.00001,.99928,1.00029,1.00092,1.00035,1.00001,1.0006,1.0006,1,.99988,.99975,1,1.00082,.99561,.9996,1.00035,1.00001,.99962,1.00001,1.00092,.99964,1.00001,.99963,.99999,1.00035,1.00035,1.00082,.99962,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.9996,.99967,1.00001,1.00034,1.00074,1.00054,1.00053,1.00063,.99971,.99962,1.00035,.99975,.99977,.99973,1.00043,.99953,1.0007,.99915,.99973,1.00008,.99892,1.00073,1.00073,1.00114,.99915,1.00073,.99955,.99973,1.00092,.99973,1,.99998,1,1.0003,1,1.00043,1.00001,.99969,1.0003,1,1.00035,1.00001,.9995,1,1.00092,.99973,.99973,.99973,1.0007,.9995,1,.99924,1.0006,.99924,.99972,1.00062,.99973,1.00114,1.00073,1,.99955,1,1,1.00047,.99968,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,.99925,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],qa={lineHeight:1.2,lineGap:.2},Oa=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,719,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,785,556,556,278,722,556,556,611,278,611,278,611,385,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,465,722,333,853,906,474,825,927,838,278,722,722,601,719,667,611,722,778,278,722,667,833,722,644,778,722,667,600,611,667,821,667,809,802,278,667,615,451,611,278,582,615,610,556,606,475,460,611,541,278,558,556,612,556,445,611,766,619,520,684,446,582,715,576,753,845,278,582,611,582,845,667,669,885,567,711,667,278,276,556,1094,1062,875,610,722,622,719,722,719,722,567,712,667,904,626,719,719,610,702,833,722,778,719,667,722,611,622,854,667,730,703,1005,1019,870,979,719,711,1031,719,556,618,615,417,635,556,709,497,615,615,500,635,740,604,611,604,611,556,490,556,875,556,615,581,833,844,729,854,615,552,854,583,556,556,611,417,552,556,278,281,278,969,906,611,500,615,556,604,778,611,487,447,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1094,556,885,489,1115,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],Pa=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Wa=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,740,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,782,556,556,278,722,556,556,611,278,611,278,611,396,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,333,722,333,854,906,473,844,930,847,278,722,722,610,671,667,611,722,778,278,722,667,833,722,657,778,718,667,590,611,667,822,667,829,781,278,667,620,479,611,278,591,620,621,556,610,479,492,611,558,278,566,556,603,556,450,611,712,605,532,664,409,591,704,578,773,834,278,591,611,591,834,667,667,886,614,719,667,278,278,556,1094,1042,854,622,719,677,719,722,708,722,614,722,667,927,643,719,719,615,687,833,722,778,719,667,722,611,677,781,667,729,708,979,989,854,1e3,708,719,1042,729,556,619,604,534,618,556,736,510,611,611,507,622,740,604,611,611,611,556,889,556,885,556,646,583,889,935,707,854,594,552,865,589,556,556,611,469,563,556,278,278,278,969,906,611,507,619,556,611,778,611,575,467,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1104,556,885,516,1146,1e3,768,600,834,834,834,834,999,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],ja=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Xa=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,625,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,733,444,500,222,667,500,500,556,222,556,222,556,281,556,400,556,222,722,556,722,556,722,556,615,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,354,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,789,846,389,794,865,775,222,667,667,570,671,667,611,722,778,278,667,667,833,722,648,778,725,667,600,611,667,837,667,831,761,278,667,570,439,555,222,550,570,571,500,556,439,463,555,542,222,500,492,548,500,447,556,670,573,486,603,374,550,652,546,728,779,222,550,556,550,779,667,667,843,544,708,667,278,278,500,1066,982,844,589,715,639,724,667,651,667,544,704,667,917,614,715,715,589,686,833,722,778,725,667,722,611,639,795,667,727,673,920,923,805,886,651,694,1022,682,556,562,522,493,553,556,688,465,556,556,472,564,686,550,556,556,556,500,833,500,835,500,572,518,830,851,621,736,526,492,752,534,556,556,556,378,496,500,222,222,222,910,828,556,472,565,500,556,778,556,492,339,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1083,1e3,768,600,834,834,834,834,1e3,500,998,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,584,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],Za=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Va=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,615,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,735,444,500,222,667,500,500,556,222,556,222,556,292,556,334,556,222,722,556,722,556,722,556,604,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,375,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,784,838,384,774,855,752,222,667,667,551,668,667,611,722,778,278,667,668,833,722,650,778,722,667,618,611,667,798,667,835,748,278,667,578,446,556,222,547,578,575,500,557,446,441,556,556,222,500,500,576,500,448,556,690,569,482,617,395,547,648,525,713,781,222,547,556,547,781,667,667,865,542,719,667,278,278,500,1057,1010,854,583,722,635,719,667,656,667,542,677,667,923,604,719,719,583,656,833,722,778,719,667,722,611,635,760,667,740,667,917,938,792,885,656,719,1010,722,556,573,531,365,583,556,669,458,559,559,438,583,688,552,556,542,556,500,458,500,823,500,573,521,802,823,625,719,521,510,750,542,556,556,556,365,510,500,222,278,222,906,812,556,438,559,500,552,778,556,489,411,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1073,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],za=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],_a=[1.36898,1,1,.72706,.80479,.83734,.98894,.99793,.9897,.93884,.86209,.94292,.94292,1.16661,1.02058,.93582,.96694,.93582,1.19137,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.78076,.78076,1.02058,1.02058,1.02058,.72851,.78966,.90838,.83637,.82391,.96376,.80061,.86275,.8768,.95407,1.0258,.73901,.85022,.83655,1.0156,.95546,.92179,.87107,.92179,.82114,.8096,.89713,.94438,.95353,.94083,.91905,.90406,.9446,.94292,1.18777,.94292,1.02058,.89903,.90088,.94938,.97898,.81093,.97571,.94938,1.024,.9577,.95933,.98621,1.0474,.97455,.98981,.9672,.95933,.9446,.97898,.97407,.97646,.78036,1.10208,.95442,.95298,.97579,.9332,.94039,.938,.80687,1.01149,.80687,1.02058,.80479,.99793,.99793,.99793,.99793,1.01149,1.00872,.90088,.91882,1.0213,.8361,1.02058,.62295,.54324,.89022,1.08595,1,1,.90088,1,.97455,.93582,.90088,1,1.05686,.8361,.99642,.99642,.99642,.72851,.90838,.90838,.90838,.90838,.90838,.90838,.868,.82391,.80061,.80061,.80061,.80061,1.0258,1.0258,1.0258,1.0258,.97484,.95546,.92179,.92179,.92179,.92179,.92179,1.02058,.92179,.94438,.94438,.94438,.94438,.90406,.86958,.98225,.94938,.94938,.94938,.94938,.94938,.94938,.9031,.81093,.94938,.94938,.94938,.94938,.98621,.98621,.98621,.98621,.93969,.95933,.9446,.9446,.9446,.9446,.9446,1.08595,.9446,.95442,.95442,.95442,.95442,.94039,.97898,.94039,.90838,.94938,.90838,.94938,.90838,.94938,.82391,.81093,.82391,.81093,.82391,.81093,.82391,.81093,.96376,.84313,.97484,.97571,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.8768,.9577,.8768,.9577,.8768,.9577,1,1,.95407,.95933,.97069,.95933,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,.887,1.01591,.73901,1.0474,1,1,.97455,.83655,.98981,1,1,.83655,.73977,.83655,.73903,.84638,1.033,.95546,.95933,1,1,.95546,.95933,.8271,.95417,.95933,.92179,.9446,.92179,.9446,.92179,.9446,.936,.91964,.82114,.97646,1,1,.82114,.97646,.8096,.78036,.8096,.78036,1,1,.8096,.78036,1,1,.89713,.77452,.89713,1.10208,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94083,.97579,.90406,.94039,.90406,.9446,.938,.9446,.938,.9446,.938,1,.99793,.90838,.94938,.868,.9031,.92179,.9446,1,1,.89713,1.10208,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90989,.9358,.91945,.83181,.75261,.87992,.82976,.96034,.83689,.97268,1.0078,.90838,.83637,.8019,.90157,.80061,.9446,.95407,.92436,1.0258,.85022,.97153,1.0156,.95546,.89192,.92179,.92361,.87107,.96318,.89713,.93704,.95638,.91905,.91709,.92796,1.0258,.93704,.94836,1.0373,.95933,1.0078,.95871,.94836,.96174,.92601,.9498,.98607,.95776,.95933,1.05453,1.0078,.98275,.9314,.95617,.91701,1.05993,.9446,.78367,.9553,1,.86832,1.0128,.95871,.99394,.87548,.96361,.86774,1.0078,.95871,.9446,.95871,.86774,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.94083,.97579,.94083,.97579,.94083,.97579,.90406,.94039,.96694,1,.89903,1,1,1,.93582,.93582,.93582,1,.908,.908,.918,.94219,.94219,.96544,1,1.285,1,1,.81079,.81079,1,1,.74854,1,1,1,1,.99793,1,1,1,.65,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.17173,1,.80535,.76169,1.02058,1.0732,1.05486,1,1,1.30692,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.16161,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],$a={lineHeight:1.2,lineGap:.2},As=[1.36898,1,1,.66227,.80779,.81625,.97276,.97276,.97733,.92222,.83266,.94292,.94292,1.16148,1.02058,.93582,.96694,.93582,1.17337,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.78076,.78076,1.02058,1.02058,1.02058,.71541,.76813,.85576,.80591,.80729,.94299,.77512,.83655,.86523,.92222,.98621,.71743,.81698,.79726,.98558,.92222,.90637,.83809,.90637,.80729,.76463,.86275,.90699,.91605,.9154,.85308,.85458,.90531,.94292,1.21296,.94292,1.02058,.89903,1.18616,.99613,.91677,.78216,.91677,.90083,.98796,.9135,.92168,.95381,.98981,.95298,.95381,.93459,.92168,.91513,.92004,.91677,.95077,.748,1.04502,.91677,.92061,.94236,.89544,.89364,.9,.80687,.8578,.80687,1.02058,.80779,.97276,.97276,.97276,.97276,.8578,.99973,1.18616,.91339,1.08074,.82891,1.02058,.55509,.71526,.89022,1.08595,1,1,1.18616,1,.96736,.93582,1.18616,1,1.04864,.82711,.99043,.99043,.99043,.71541,.85576,.85576,.85576,.85576,.85576,.85576,.845,.80729,.77512,.77512,.77512,.77512,.98621,.98621,.98621,.98621,.95961,.92222,.90637,.90637,.90637,.90637,.90637,1.02058,.90251,.90699,.90699,.90699,.90699,.85458,.83659,.94951,.99613,.99613,.99613,.99613,.99613,.99613,.85811,.78216,.90083,.90083,.90083,.90083,.95381,.95381,.95381,.95381,.9135,.92168,.91513,.91513,.91513,.91513,.91513,1.08595,.91677,.91677,.91677,.91677,.91677,.89364,.92332,.89364,.85576,.99613,.85576,.99613,.85576,.99613,.80729,.78216,.80729,.78216,.80729,.78216,.80729,.78216,.94299,.76783,.95961,.91677,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.86523,.9135,.86523,.9135,.86523,.9135,1,1,.92222,.92168,.92222,.92168,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.86036,.97096,.71743,.98981,1,1,.95298,.79726,.95381,1,1,.79726,.6894,.79726,.74321,.81691,1.0006,.92222,.92168,1,1,.92222,.92168,.79464,.92098,.92168,.90637,.91513,.90637,.91513,.90637,.91513,.909,.87514,.80729,.95077,1,1,.80729,.95077,.76463,.748,.76463,.748,1,1,.76463,.748,1,1,.86275,.72651,.86275,1.04502,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.9154,.94236,.85458,.89364,.85458,.90531,.9,.90531,.9,.90531,.9,1,.97276,.85576,.99613,.845,.85811,.90251,.91677,1,1,.86275,1.04502,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.00899,1.30628,.85576,.80178,.66862,.7927,.69323,.88127,.72459,.89711,.95381,.85576,.80591,.7805,.94729,.77512,.90531,.92222,.90637,.98621,.81698,.92655,.98558,.92222,.85359,.90637,.90976,.83809,.94523,.86275,.83509,.93157,.85308,.83392,.92346,.98621,.83509,.92886,.91324,.92168,.95381,.90646,.92886,.90557,.86847,.90276,.91324,.86842,.92168,.99531,.95381,.9224,.85408,.92699,.86847,1.0051,.91513,.80487,.93481,1,.88159,1.05214,.90646,.97355,.81539,.89398,.85923,.95381,.90646,.91513,.90646,.85923,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9154,.94236,.9154,.94236,.9154,.94236,.85458,.89364,.96694,1,.89903,1,1,1,.91782,.91782,.91782,1,.896,.896,.896,.9332,.9332,.95973,1,1.26,1,1,.80479,.80178,1,1,.85633,1,1,1,1,.97276,1,1,1,.698,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.14542,1,.79199,.78694,1.02058,1.03493,1.05486,1,1,1.23026,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.20006,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],es={lineHeight:1.2,lineGap:.2},ts=[1.36898,1,1,.65507,.84943,.85639,.88465,.88465,.86936,.88307,.86948,.85283,.85283,1.06383,1.02058,.75945,.9219,.75945,1.17337,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.75945,.75945,1.02058,1.02058,1.02058,.69046,.70926,.85158,.77812,.76852,.89591,.70466,.76125,.80094,.86822,.83864,.728,.77212,.79475,.93637,.87514,.8588,.76013,.8588,.72421,.69866,.77598,.85991,.80811,.87832,.78112,.77512,.8562,1.0222,1.18417,1.0222,1.27014,.89903,1.15012,.93859,.94399,.846,.94399,.81453,1.0186,.94219,.96017,1.03075,1.02175,.912,1.03075,.96998,.96017,.93859,.94399,.94399,.95493,.746,1.12658,.94578,.91,.979,.882,.882,.83,.85034,.83537,.85034,1.02058,.70869,.88465,.88465,.88465,.88465,.83537,.90083,1.15012,.9161,.94565,.73541,1.02058,.53609,.69353,.79519,1.08595,1,1,1.15012,1,.91974,.75945,1.15012,1,.9446,.73361,.9005,.9005,.9005,.62864,.85158,.85158,.85158,.85158,.85158,.85158,.773,.76852,.70466,.70466,.70466,.70466,.83864,.83864,.83864,.83864,.90561,.87514,.8588,.8588,.8588,.8588,.8588,1.02058,.85751,.85991,.85991,.85991,.85991,.77512,.76013,.88075,.93859,.93859,.93859,.93859,.93859,.93859,.8075,.846,.81453,.81453,.81453,.81453,.82424,.82424,.82424,.82424,.9278,.96017,.93859,.93859,.93859,.93859,.93859,1.08595,.8562,.94578,.94578,.94578,.94578,.882,.94578,.882,.85158,.93859,.85158,.93859,.85158,.93859,.76852,.846,.76852,.846,.76852,.846,.76852,.846,.89591,.8544,.90561,.94399,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.80094,.94219,.80094,.94219,.80094,.94219,1,1,.86822,.96017,.86822,.96017,.83864,.82424,.83864,.82424,.83864,.82424,.83864,1.03075,.83864,.82424,.81402,1.02738,.728,1.02175,1,1,.912,.79475,1.03075,1,1,.79475,.83911,.79475,.66266,.80553,1.06676,.87514,.96017,1,1,.87514,.96017,.86865,.87396,.96017,.8588,.93859,.8588,.93859,.8588,.93859,.867,.84759,.72421,.95493,1,1,.72421,.95493,.69866,.746,.69866,.746,1,1,.69866,.746,1,1,.77598,.88417,.77598,1.12658,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.87832,.979,.77512,.882,.77512,.8562,.83,.8562,.83,.8562,.83,1,.88465,.85158,.93859,.773,.8075,.85751,.8562,1,1,.77598,1.12658,1.15012,1.15012,1.15012,1.15012,1.15012,1.15313,1.15012,1.15012,1.15012,1.08106,1.03901,.85158,.77025,.62264,.7646,.65351,.86026,.69461,.89947,1.03075,.85158,.77812,.76449,.88836,.70466,.8562,.86822,.8588,.83864,.77212,.85308,.93637,.87514,.82352,.8588,.85701,.76013,.89058,.77598,.8156,.82565,.78112,.77899,.89386,.83864,.8156,.9486,.92388,.96186,1.03075,.91123,.9486,.93298,.878,.93942,.92388,.84596,.96186,.95119,1.03075,.922,.88787,.95829,.88,.93559,.93859,.78815,.93758,1,.89217,1.03737,.91123,.93969,.77487,.85769,.86799,1.03075,.91123,.93859,.91123,.86799,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87832,.979,.87832,.979,.87832,.979,.77512,.882,.9219,1,.89903,1,1,1,.87321,.87321,.87321,1,1.027,1.027,1.027,.86847,.86847,.79121,1,1.124,1,1,.73572,.73572,1,1,.85034,1,1,1,1,.88465,1,1,1,.669,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.04828,1,.74948,.75187,1.02058,.98391,1.02119,1,1,1.06233,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05233,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],is={lineHeight:1.2,lineGap:.2},as=[1.36898,1,1,.76305,.82784,.94935,.89364,.92241,.89073,.90706,.98472,.85283,.85283,1.0664,1.02058,.74505,.9219,.74505,1.23456,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.74505,.74505,1.02058,1.02058,1.02058,.73002,.72601,.91755,.8126,.80314,.92222,.73764,.79726,.83051,.90284,.86023,.74,.8126,.84869,.96518,.91115,.8858,.79761,.8858,.74498,.73914,.81363,.89591,.83659,.89633,.85608,.8111,.90531,1.0222,1.22736,1.0222,1.27014,.89903,.90088,.86667,1.0231,.896,1.01411,.90083,1.05099,1.00512,.99793,1.05326,1.09377,.938,1.06226,1.00119,.99793,.98714,1.0231,1.01231,.98196,.792,1.19137,.99074,.962,1.01915,.926,.942,.856,.85034,.92006,.85034,1.02058,.69067,.92241,.92241,.92241,.92241,.92006,.9332,.90088,.91882,.93484,.75339,1.02058,.56866,.54324,.79519,1.08595,1,1,.90088,1,.95325,.74505,.90088,1,.97198,.75339,.91009,.91009,.91009,.66466,.91755,.91755,.91755,.91755,.91755,.91755,.788,.80314,.73764,.73764,.73764,.73764,.86023,.86023,.86023,.86023,.92915,.91115,.8858,.8858,.8858,.8858,.8858,1.02058,.8858,.89591,.89591,.89591,.89591,.8111,.79611,.89713,.86667,.86667,.86667,.86667,.86667,.86667,.86936,.896,.90083,.90083,.90083,.90083,.84224,.84224,.84224,.84224,.97276,.99793,.98714,.98714,.98714,.98714,.98714,1.08595,.89876,.99074,.99074,.99074,.99074,.942,1.0231,.942,.91755,.86667,.91755,.86667,.91755,.86667,.80314,.896,.80314,.896,.80314,.896,.80314,.896,.92222,.93372,.92915,1.01411,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.83051,1.00512,.83051,1.00512,.83051,1.00512,1,1,.90284,.99793,.90976,.99793,.86023,.84224,.86023,.84224,.86023,.84224,.86023,1.05326,.86023,.84224,.82873,1.07469,.74,1.09377,1,1,.938,.84869,1.06226,1,1,.84869,.83704,.84869,.81441,.85588,1.08927,.91115,.99793,1,1,.91115,.99793,.91887,.90991,.99793,.8858,.98714,.8858,.98714,.8858,.98714,.894,.91434,.74498,.98196,1,1,.74498,.98196,.73914,.792,.73914,.792,1,1,.73914,.792,1,1,.81363,.904,.81363,1.19137,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89633,1.01915,.8111,.942,.8111,.90531,.856,.90531,.856,.90531,.856,1,.92241,.91755,.86667,.788,.86936,.8858,.89876,1,1,.81363,1.19137,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90388,1.03901,.92138,.78105,.7154,.86169,.80513,.94007,.82528,.98612,1.06226,.91755,.8126,.81884,.92819,.73764,.90531,.90284,.8858,.86023,.8126,.91172,.96518,.91115,.83089,.8858,.87791,.79761,.89297,.81363,.88157,.89992,.85608,.81992,.94307,.86023,.88157,.95308,.98699,.99793,1.06226,.95817,.95308,.97358,.928,.98088,.98699,.92761,.99793,.96017,1.06226,.986,.944,.95978,.938,.96705,.98714,.80442,.98972,1,.89762,1.04552,.95817,.99007,.87064,.91879,.88888,1.06226,.95817,.98714,.95817,.88888,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89633,1.01915,.89633,1.01915,.89633,1.01915,.8111,.942,.9219,1,.89903,1,1,1,.93173,.93173,.93173,1,1.06304,1.06304,1.06904,.89903,.89903,.80549,1,1.156,1,1,.76575,.76575,1,1,.72458,1,1,1,1,.92241,1,1,1,.619,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.07257,1,.74705,.71119,1.02058,1.024,1.02119,1,1,1.1536,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05638,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ss={lineHeight:1.2,lineGap:.2},rs=[1.76738,1,1,.99297,.9824,1.04016,1.06497,1.03424,.97529,1.17647,1.23203,1.1085,1.1085,1.16939,1.2107,.9754,1.21408,.9754,1.59578,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,.81378,.81378,1.2107,1.2107,1.2107,.71703,.97847,.97363,.88776,.8641,1.02096,.79795,.85132,.914,1.06085,1.1406,.8007,.89858,.83693,1.14889,1.09398,.97489,.92094,.97489,.90399,.84041,.95923,1.00135,1,1.06467,.98243,.90996,.99361,1.1085,1.56942,1.1085,1.2107,.74627,.94282,.96752,1.01519,.86304,1.01359,.97278,1.15103,1.01359,.98561,1.02285,1.02285,1.00527,1.02285,1.0302,.99041,1.0008,1.01519,1.01359,1.02258,.79104,1.16862,.99041,.97454,1.02511,.99298,.96752,.95801,.94856,1.16579,.94856,1.2107,.9824,1.03424,1.03424,1,1.03424,1.16579,.8727,1.3871,1.18622,1.10818,1.04478,1.2107,1.18622,.75155,.94994,1.28826,1.21408,1.21408,.91056,1,.91572,.9754,.64663,1.18328,1.24866,1.04478,1.14169,1.15749,1.17389,.71703,.97363,.97363,.97363,.97363,.97363,.97363,.93506,.8641,.79795,.79795,.79795,.79795,1.1406,1.1406,1.1406,1.1406,1.02096,1.09398,.97426,.97426,.97426,.97426,.97426,1.2107,.97489,1.00135,1.00135,1.00135,1.00135,.90996,.92094,1.02798,.96752,.96752,.96752,.96752,.96752,.96752,.93136,.86304,.97278,.97278,.97278,.97278,1.02285,1.02285,1.02285,1.02285,.97122,.99041,1,1,1,1,1,1.28826,1.0008,.99041,.99041,.99041,.99041,.96752,1.01519,.96752,.97363,.96752,.97363,.96752,.97363,.96752,.8641,.86304,.8641,.86304,.8641,.86304,.8641,.86304,1.02096,1.03057,1.02096,1.03517,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.914,1.01359,.914,1.01359,.914,1.01359,1,1,1.06085,.98561,1.06085,1.00879,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,.97138,1.08692,.8007,1.02285,1,1,1.00527,.83693,1.02285,1,1,.83693,.9455,.83693,.90418,.83693,1.13005,1.09398,.99041,1,1,1.09398,.99041,.96692,1.09251,.99041,.97489,1.0008,.97489,1.0008,.97489,1.0008,.93994,.97931,.90399,1.02258,1,1,.90399,1.02258,.84041,.79104,.84041,.79104,.84041,.79104,.84041,.79104,1,1,.95923,1.07034,.95923,1.16862,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.06467,1.02511,.90996,.96752,.90996,.99361,.95801,.99361,.95801,.99361,.95801,1.07733,1.03424,.97363,.96752,.93506,.93136,.97489,1.0008,1,1,.95923,1.16862,1.15103,1.15103,1.01173,1.03959,.75953,.81378,.79912,1.15103,1.21994,.95161,.87815,1.01149,.81525,.7676,.98167,1.01134,1.02546,.84097,1.03089,1.18102,.97363,.88776,.85134,.97826,.79795,.99361,1.06085,.97489,1.1406,.89858,1.0388,1.14889,1.09398,.86039,.97489,1.0595,.92094,.94793,.95923,.90996,.99346,.98243,1.02112,.95493,1.1406,.90996,1.03574,1.02597,1.0008,1.18102,1.06628,1.03574,1.0192,1.01932,1.00886,.97531,1.0106,1.0008,1.13189,1.18102,1.02277,.98683,1.0016,.99561,1.07237,1.0008,.90434,.99921,.93803,.8965,1.23085,1.06628,1.04983,.96268,1.0499,.98439,1.18102,1.06628,1.0008,1.06628,.98439,.79795,1,1,1,1,1,1,1,1,1,1,1,1,1.09466,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.97278,1,1,1,1,1,1,1,1,1,1,1,1,1.02065,1,1,1,1,1,1,1.06467,1.02511,1.06467,1.02511,1.06467,1.02511,.90996,.96752,1,1.21408,.89903,1,1,.75155,1.04394,1.04394,1.04394,1.04394,.98633,.98633,.98633,.73047,.73047,1.20642,.91211,1.25635,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.12454,.93503,1.03424,1.19687,1.03424,1,1,1,.771,1,1,1.15749,1.15749,1.15749,1.10948,.86279,.94434,.86279,.94434,.86182,1,1,1.16897,1,.96085,.90137,1.2107,1.18416,1.13973,.69825,.9716,2.10339,1.29004,1.29004,1.21172,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18874,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.09193,1.09193,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ns={lineHeight:1.33008,lineGap:0},gs=[1.76738,1,1,.98946,1.03959,1.04016,1.02809,1.036,.97639,1.10953,1.23203,1.11144,1.11144,1.16939,1.21237,.9754,1.21261,.9754,1.59754,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,.81378,.81378,1.21237,1.21237,1.21237,.73541,.97847,.97363,.89723,.87897,1.0426,.79429,.85292,.91149,1.05815,1.1406,.79631,.90128,.83853,1.04396,1.10615,.97552,.94436,.97552,.88641,.80527,.96083,1.00135,1,1.06777,.9817,.91142,.99361,1.11144,1.57293,1.11144,1.21237,.74627,1.31818,1.06585,.97042,.83055,.97042,.93503,1.1261,.97042,.97922,1.14236,.94552,1.01054,1.14236,1.02471,.97922,.94165,.97042,.97042,1.0276,.78929,1.1261,.97922,.95874,1.02197,.98507,.96752,.97168,.95107,1.16579,.95107,1.21237,1.03959,1.036,1.036,1,1.036,1.16579,.87357,1.31818,1.18754,1.26781,1.05356,1.21237,1.18622,.79487,.94994,1.29004,1.24047,1.24047,1.31818,1,.91484,.9754,1.31818,1.1349,1.24866,1.05356,1.13934,1.15574,1.17389,.73541,.97363,.97363,.97363,.97363,.97363,.97363,.94385,.87897,.79429,.79429,.79429,.79429,1.1406,1.1406,1.1406,1.1406,1.0426,1.10615,.97552,.97552,.97552,.97552,.97552,1.21237,.97552,1.00135,1.00135,1.00135,1.00135,.91142,.94436,.98721,1.06585,1.06585,1.06585,1.06585,1.06585,1.06585,.96705,.83055,.93503,.93503,.93503,.93503,1.14236,1.14236,1.14236,1.14236,.93125,.97922,.94165,.94165,.94165,.94165,.94165,1.29004,.94165,.97922,.97922,.97922,.97922,.96752,.97042,.96752,.97363,1.06585,.97363,1.06585,.97363,1.06585,.87897,.83055,.87897,.83055,.87897,.83055,.87897,.83055,1.0426,1.0033,1.0426,.97042,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.91149,.97042,.91149,.97042,.91149,.97042,1,1,1.05815,.97922,1.05815,.97922,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,.97441,1.04302,.79631,1.01582,1,1,1.01054,.83853,1.14236,1,1,.83853,1.09125,.83853,.90418,.83853,1.19508,1.10615,.97922,1,1,1.10615,.97922,1.01034,1.10466,.97922,.97552,.94165,.97552,.94165,.97552,.94165,.91602,.91981,.88641,1.0276,1,1,.88641,1.0276,.80527,.78929,.80527,.78929,.80527,.78929,.80527,.78929,1,1,.96083,1.05403,.95923,1.16862,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.06777,1.02197,.91142,.96752,.91142,.99361,.97168,.99361,.97168,.99361,.97168,1.23199,1.036,.97363,1.06585,.94385,.96705,.97552,.94165,1,1,.96083,1.1261,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,.95161,1.27126,1.00811,.83284,.77702,.99137,.95253,1.0347,.86142,1.07205,1.14236,.97363,.89723,.86869,1.09818,.79429,.99361,1.05815,.97552,1.1406,.90128,1.06662,1.04396,1.10615,.84918,.97552,1.04694,.94436,.98015,.96083,.91142,1.00356,.9817,1.01945,.98999,1.1406,.91142,1.04961,.9898,1.00639,1.14236,1.07514,1.04961,.99607,1.02897,1.008,.9898,.95134,1.00639,1.11121,1.14236,1.00518,.97981,1.02186,1,1.08578,.94165,.99314,.98387,.93028,.93377,1.35125,1.07514,1.10687,.93491,1.04232,1.00351,1.14236,1.07514,.94165,1.07514,1.00351,.79429,1,1,1,1,1,1,1,1,1,1,1,1,1.09097,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.93503,1,1,1,1,1,1,1,1,1,1,1,1,.96609,1,1,1,1,1,1,1.06777,1.02197,1.06777,1.02197,1.06777,1.02197,.91142,.96752,1,1.21261,.89903,1,1,.75155,1.04745,1.04745,1.04745,1.04394,.98633,.98633,.98633,.72959,.72959,1.20502,.91406,1.26514,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.09125,.93327,1.03336,1.16541,1.036,1,1,1,.771,1,1,1.15574,1.15574,1.15574,1.15574,.86364,.94434,.86279,.94434,.86224,1,1,1.16798,1,.96085,.90068,1.21237,1.18416,1.13904,.69825,.9716,2.10339,1.29004,1.29004,1.21339,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18775,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.13269,1.13269,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],os={lineHeight:1.33008,lineGap:0},Is=[1.76738,1,1,.98946,1.14763,1.05365,1.06234,.96927,.92586,1.15373,1.18414,.91349,.91349,1.07403,1.17308,.78383,1.20088,.78383,1.42531,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78383,.78383,1.17308,1.17308,1.17308,.77349,.94565,.94729,.85944,.88506,.9858,.74817,.80016,.88449,.98039,.95782,.69238,.89898,.83231,.98183,1.03989,.96924,.86237,.96924,.80595,.74524,.86091,.95402,.94143,.98448,.8858,.83089,.93285,1.0949,1.39016,1.0949,1.45994,.74627,1.04839,.97454,.97454,.87207,.97454,.87533,1.06151,.97454,1.00176,1.16484,1.08132,.98047,1.16484,1.02989,1.01054,.96225,.97454,.97454,1.06598,.79004,1.16344,1.00351,.94629,.9973,.91016,.96777,.9043,.91082,.92481,.91082,1.17308,.95748,.96927,.96927,1,.96927,.92481,.80597,1.04839,1.23393,1.1781,.9245,1.17308,1.20808,.63218,.94261,1.24822,1.09971,1.09971,1.04839,1,.85273,.78032,1.04839,1.09971,1.22326,.9245,1.09836,1.13525,1.15222,.70424,.94729,.94729,.94729,.94729,.94729,.94729,.85498,.88506,.74817,.74817,.74817,.74817,.95782,.95782,.95782,.95782,.9858,1.03989,.96924,.96924,.96924,.96924,.96924,1.17308,.96924,.95402,.95402,.95402,.95402,.83089,.86237,.88409,.97454,.97454,.97454,.97454,.97454,.97454,.92916,.87207,.87533,.87533,.87533,.87533,.93146,.93146,.93146,.93146,.93854,1.01054,.96225,.96225,.96225,.96225,.96225,1.24822,.8761,1.00351,1.00351,1.00351,1.00351,.96777,.97454,.96777,.94729,.97454,.94729,.97454,.94729,.97454,.88506,.87207,.88506,.87207,.88506,.87207,.88506,.87207,.9858,.95391,.9858,.97454,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.88449,.97454,.88449,.97454,.88449,.97454,1,1,.98039,1.00176,.98039,1.00176,.95782,.93146,.95782,.93146,.95782,.93146,.95782,1.16484,.95782,.93146,.84421,1.12761,.69238,1.08132,1,1,.98047,.83231,1.16484,1,1,.84723,1.04861,.84723,.78755,.83231,1.23736,1.03989,1.01054,1,1,1.03989,1.01054,.9857,1.03849,1.01054,.96924,.96225,.96924,.96225,.96924,.96225,.92383,.90171,.80595,1.06598,1,1,.80595,1.06598,.74524,.79004,.74524,.79004,.74524,.79004,.74524,.79004,1,1,.86091,1.02759,.85771,1.16344,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.98448,.9973,.83089,.96777,.83089,.93285,.9043,.93285,.9043,.93285,.9043,1.31868,.96927,.94729,.97454,.85498,.92916,.96924,.8761,1,1,.86091,1.16344,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,.81965,.81965,.94729,.78032,.71022,.90883,.84171,.99877,.77596,1.05734,1.2,.94729,.85944,.82791,.9607,.74817,.93285,.98039,.96924,.95782,.89898,.98316,.98183,1.03989,.78614,.96924,.97642,.86237,.86075,.86091,.83089,.90082,.8858,.97296,1.01284,.95782,.83089,1.0976,1.04,1.03342,1.2,1.0675,1.0976,.98205,1.03809,1.05097,1.04,.95364,1.03342,1.05401,1.2,1.02148,1.0119,1.04724,1.0127,1.02732,.96225,.8965,.97783,.93574,.94818,1.30679,1.0675,1.11826,.99821,1.0557,1.0326,1.2,1.0675,.96225,1.0675,1.0326,.74817,1,1,1,1,1,1,1,1,1,1,1,1,1.03754,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87533,1,1,1,1,1,1,1,1,1,1,1,1,.98705,1,1,1,1,1,1,.98448,.9973,.98448,.9973,.98448,.9973,.83089,.96777,1,1.20088,.89903,1,1,.75155,.94945,.94945,.94945,.94945,1.12317,1.12317,1.12317,.67603,.67603,1.15621,.73584,1.21191,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87709,.96927,1.01473,.96927,1,1,1,.77295,1,1,1.09836,1.09836,1.09836,1.01522,.86321,.94434,.8649,.94434,.86182,1,1,1.083,1,.91578,.86438,1.17308,1.18416,1.14589,.69825,.97622,1.96791,1.24822,1.24822,1.17308,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.17984,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10742,1.10742,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],cs={lineHeight:1.33008,lineGap:0},Cs=[1.76738,1,1,.98594,1.02285,1.10454,1.06234,.96927,.92037,1.19985,1.2046,.90616,.90616,1.07152,1.1714,.78032,1.20088,.78032,1.40246,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78032,.78032,1.1714,1.1714,1.1714,.80597,.94084,.96706,.85944,.85734,.97093,.75842,.79936,.88198,.9831,.95782,.71387,.86969,.84636,1.07796,1.03584,.96924,.83968,.96924,.82826,.79649,.85771,.95132,.93119,.98965,.88433,.8287,.93365,1.08612,1.3638,1.08612,1.45786,.74627,.80499,.91484,1.05707,.92383,1.05882,.9403,1.12654,1.05882,1.01756,1.09011,1.09011,.99414,1.09011,1.034,1.01756,1.05356,1.05707,1.05882,1.04399,.84863,1.21968,1.01756,.95801,1.00068,.91797,.96777,.9043,.90351,.92105,.90351,1.1714,.85337,.96927,.96927,.99912,.96927,.92105,.80597,1.2434,1.20808,1.05937,.90957,1.1714,1.20808,.75155,.94261,1.24644,1.09971,1.09971,.84751,1,.85273,.78032,.61584,1.05425,1.17914,.90957,1.08665,1.11593,1.14169,.73381,.96706,.96706,.96706,.96706,.96706,.96706,.86035,.85734,.75842,.75842,.75842,.75842,.95782,.95782,.95782,.95782,.97093,1.03584,.96924,.96924,.96924,.96924,.96924,1.1714,.96924,.95132,.95132,.95132,.95132,.8287,.83968,.89049,.91484,.91484,.91484,.91484,.91484,.91484,.93575,.92383,.9403,.9403,.9403,.9403,.8717,.8717,.8717,.8717,1.00527,1.01756,1.05356,1.05356,1.05356,1.05356,1.05356,1.24644,.95923,1.01756,1.01756,1.01756,1.01756,.96777,1.05707,.96777,.96706,.91484,.96706,.91484,.96706,.91484,.85734,.92383,.85734,.92383,.85734,.92383,.85734,.92383,.97093,1.0969,.97093,1.05882,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.88198,1.05882,.88198,1.05882,.88198,1.05882,1,1,.9831,1.01756,.9831,1.01756,.95782,.8717,.95782,.8717,.95782,.8717,.95782,1.09011,.95782,.8717,.84784,1.11551,.71387,1.09011,1,1,.99414,.84636,1.09011,1,1,.84636,1.0536,.84636,.94298,.84636,1.23297,1.03584,1.01756,1,1,1.03584,1.01756,1.00323,1.03444,1.01756,.96924,1.05356,.96924,1.05356,.96924,1.05356,.93066,.98293,.82826,1.04399,1,1,.82826,1.04399,.79649,.84863,.79649,.84863,.79649,.84863,.79649,.84863,1,1,.85771,1.17318,.85771,1.21968,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.98965,1.00068,.8287,.96777,.8287,.93365,.9043,.93365,.9043,.93365,.9043,1.08571,.96927,.96706,.91484,.86035,.93575,.96924,.95923,1,1,.85771,1.21968,1.11437,1.11437,.93109,.91202,.60411,.84164,.55572,1.01173,.97361,.81818,.81818,.96635,.78032,.72727,.92366,.98601,1.03405,.77968,1.09799,1.2,.96706,.85944,.85638,.96491,.75842,.93365,.9831,.96924,.95782,.86969,.94152,1.07796,1.03584,.78437,.96924,.98715,.83968,.83491,.85771,.8287,.94492,.88433,.9287,1.0098,.95782,.8287,1.0625,.98248,1.03424,1.2,1.01071,1.0625,.95246,1.03809,1.04912,.98248,1.00221,1.03424,1.05443,1.2,1.04785,.99609,1.00169,1.05176,.99346,1.05356,.9087,1.03004,.95542,.93117,1.23362,1.01071,1.07831,1.02512,1.05205,1.03502,1.2,1.01071,1.05356,1.01071,1.03502,.75842,1,1,1,1,1,1,1,1,1,1,1,1,1.03719,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9403,1,1,1,1,1,1,1,1,1,1,1,1,1.04021,1,1,1,1,1,1,.98965,1.00068,.98965,1.00068,.98965,1.00068,.8287,.96777,1,1.20088,.89903,1,1,.75155,1.03077,1.03077,1.03077,1.03077,1.13196,1.13196,1.13196,.67428,.67428,1.16039,.73291,1.20996,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87796,.96927,1.01518,.96927,1,1,1,.77295,1,1,1.10539,1.10539,1.11358,1.06967,.86279,.94434,.86279,.94434,.86182,1,1,1.083,1,.91578,.86507,1.1714,1.18416,1.14589,.69825,.97622,1.9697,1.24822,1.24822,1.17238,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18083,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10938,1.10938,1,1,1,1.05425,1.09971,1.09971,1.09971,1,1,1,1,1,1,1,1,1,1,1],hs={lineHeight:1.33008,lineGap:0},Bs=getLookupTableFactory((function(e){e["MyriadPro-Regular"]=e["PdfJS-Fallback-Regular"]={name:"LiberationSans-Regular",factors:as,baseWidths:Va,baseMapping:za,metrics:ss};e["MyriadPro-Bold"]=e["PdfJS-Fallback-Bold"]={name:"LiberationSans-Bold",factors:_a,baseWidths:Oa,baseMapping:Pa,metrics:$a};e["MyriadPro-It"]=e["MyriadPro-Italic"]=e["PdfJS-Fallback-Italic"]={name:"LiberationSans-Italic",factors:ts,baseWidths:Xa,baseMapping:Za,metrics:is};e["MyriadPro-BoldIt"]=e["MyriadPro-BoldItalic"]=e["PdfJS-Fallback-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:As,baseWidths:Wa,baseMapping:ja,metrics:es};e.ArialMT=e.Arial=e["Arial-Regular"]={name:"LiberationSans-Regular",baseWidths:Va,baseMapping:za};e["Arial-BoldMT"]=e["Arial-Bold"]={name:"LiberationSans-Bold",baseWidths:Oa,baseMapping:Pa};e["Arial-ItalicMT"]=e["Arial-Italic"]={name:"LiberationSans-Italic",baseWidths:Xa,baseMapping:Za};e["Arial-BoldItalicMT"]=e["Arial-BoldItalic"]={name:"LiberationSans-BoldItalic",baseWidths:Wa,baseMapping:ja};e["Calibri-Regular"]={name:"LiberationSans-Regular",factors:Ua,baseWidths:Va,baseMapping:za,metrics:Ma};e["Calibri-Bold"]={name:"LiberationSans-Bold",factors:Sa,baseWidths:Oa,baseMapping:Pa,metrics:ka};e["Calibri-Italic"]={name:"LiberationSans-Italic",factors:Ga,baseWidths:Xa,baseMapping:Za,metrics:xa};e["Calibri-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Ra,baseWidths:Wa,baseMapping:ja,metrics:Na};e["Segoeui-Regular"]={name:"LiberationSans-Regular",factors:Cs,baseWidths:Va,baseMapping:za,metrics:hs};e["Segoeui-Bold"]={name:"LiberationSans-Bold",factors:rs,baseWidths:Oa,baseMapping:Pa,metrics:ns};e["Segoeui-Italic"]={name:"LiberationSans-Italic",factors:Is,baseWidths:Xa,baseMapping:Za,metrics:cs};e["Segoeui-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:gs,baseWidths:Wa,baseMapping:ja,metrics:os};e["Helvetica-Regular"]=e.Helvetica={name:"LiberationSans-Regular",factors:Ta,baseWidths:Va,baseMapping:za,metrics:qa};e["Helvetica-Bold"]={name:"LiberationSans-Bold",factors:La,baseWidths:Oa,baseMapping:Pa,metrics:Ha};e["Helvetica-Italic"]={name:"LiberationSans-Italic",factors:va,baseWidths:Xa,baseMapping:Za,metrics:Ka};e["Helvetica-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Ja,baseWidths:Wa,baseMapping:ja,metrics:Ya}}));function getXfaFontName(e){const t=normalizeFontName(e);return Bs()[t]}function getXfaFontDict(e){const t=function getXfaFontWidths(e){const t=getXfaFontName(e);if(!t)return null;const{baseWidths:i,baseMapping:a,factors:s}=t,r=s?i.map(((e,t)=>e*s[t])):i;let n,g=-2;const o=[];for(const[e,t]of a.map(((e,t)=>[e,t])).sort((([e],[t])=>e-t)))if(-1!==e)if(e===g+1){n.push(r[t]);g+=1}else{g=e;n=[r[t]];o.push(e,n)}return o}(e),i=new Dict(null);i.set("BaseFont",Name.get(e));i.set("Type",Name.get("Font"));i.set("Subtype",Name.get("CIDFontType2"));i.set("Encoding",Name.get("Identity-H"));i.set("CIDToGIDMap",Name.get("Identity"));i.set("W",t);i.set("FirstChar",t[0]);i.set("LastChar",t.at(-2)+t.at(-1).length-1);const a=new Dict(null);i.set("FontDescriptor",a);const s=new Dict(null);s.set("Ordering","Identity");s.set("Registry","Adobe");s.set("Supplement",0);i.set("CIDSystemInfo",s);return i}class PostScriptParser{constructor(e){this.lexer=e;this.operators=[];this.token=null;this.prev=null}nextToken(){this.prev=this.token;this.token=this.lexer.getToken()}accept(e){if(this.token.type===e){this.nextToken();return!0}return!1}expect(e){if(this.accept(e))return!0;throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${e}.`)}parse(){this.nextToken();this.expect(ls.LBRACE);this.parseBlock();this.expect(ls.RBRACE);return this.operators}parseBlock(){for(;;)if(this.accept(ls.NUMBER))this.operators.push(this.prev.value);else if(this.accept(ls.OPERATOR))this.operators.push(this.prev.value);else{if(!this.accept(ls.LBRACE))return;this.parseCondition()}}parseCondition(){const e=this.operators.length;this.operators.push(null,null);this.parseBlock();this.expect(ls.RBRACE);if(this.accept(ls.IF)){this.operators[e]=this.operators.length;this.operators[e+1]="jz"}else{if(!this.accept(ls.LBRACE))throw new FormatError("PS Function: error parsing conditional.");{const t=this.operators.length;this.operators.push(null,null);const i=this.operators.length;this.parseBlock();this.expect(ls.RBRACE);this.expect(ls.IFELSE);this.operators[t]=this.operators.length;this.operators[t+1]="j";this.operators[e]=i;this.operators[e+1]="jz"}}}}const ls={LBRACE:0,RBRACE:1,NUMBER:2,OPERATOR:3,IF:4,IFELSE:5};class PostScriptToken{static get opCache(){return shadow(this,"opCache",Object.create(null))}constructor(e,t){this.type=e;this.value=t}static getOperator(e){return PostScriptToken.opCache[e]||=new PostScriptToken(ls.OPERATOR,e)}static get LBRACE(){return shadow(this,"LBRACE",new PostScriptToken(ls.LBRACE,"{"))}static get RBRACE(){return shadow(this,"RBRACE",new PostScriptToken(ls.RBRACE,"}"))}static get IF(){return shadow(this,"IF",new PostScriptToken(ls.IF,"IF"))}static get IFELSE(){return shadow(this,"IFELSE",new PostScriptToken(ls.IFELSE,"IFELSE"))}}class PostScriptLexer{constructor(e){this.stream=e;this.nextChar();this.strBuf=[]}nextChar(){return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(t<0)return wt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return new PostScriptToken(ls.NUMBER,this.getNumber());case 123:this.nextChar();return PostScriptToken.LBRACE;case 125:this.nextChar();return PostScriptToken.RBRACE}const i=this.strBuf;i.length=0;i[0]=String.fromCharCode(t);for(;(t=this.nextChar())>=0&&(t>=65&&t<=90||t>=97&&t<=122);)i.push(String.fromCharCode(t));const a=i.join("");switch(a.toLowerCase()){case"if":return PostScriptToken.IF;case"ifelse":return PostScriptToken.IFELSE;default:return PostScriptToken.getOperator(a)}}getNumber(){let e=this.currentChar;const t=this.strBuf;t.length=0;t[0]=String.fromCharCode(e);for(;(e=this.nextChar())>=0&&(e>=48&&e<=57||45===e||46===e);)t.push(String.fromCharCode(e));const i=parseFloat(t.join(""));if(isNaN(i))throw new FormatError(`Invalid floating point number: ${i}`);return i}}class BaseLocalCache{constructor(e){this._onlyRefs=!0===e?.onlyRefs;if(!this._onlyRefs){this._nameRefMap=new Map;this._imageMap=new Map}this._imageCache=new RefSetCache}getByName(e){this._onlyRefs&&unreachable("Should not call `getByName` method.");const t=this._nameRefMap.get(e);return t?this.getByRef(t):this._imageMap.get(e)||null}getByRef(e){return this._imageCache.get(e)||null}set(e,t,i){unreachable("Abstract method `set` called.")}}class LocalImageCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalImageCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalColorSpaceCache extends BaseLocalCache{set(e=null,t=null,i){if("string"!=typeof e&&!t)throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.');if(t){if(this._imageCache.has(t))return;null!==e&&this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalFunctionCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalFunctionCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class LocalGStateCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalGStateCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalTilingPatternCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalTilingPatternCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class RegionalImageCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('RegionalImageCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class GlobalImageCache{static NUM_PAGES_THRESHOLD=2;static MIN_IMAGES_TO_CACHE=10;static MAX_BYTE_SIZE=5e7;#D=new RefSet;constructor(){this._refCache=new RefSetCache;this._imageCache=new RefSetCache}get#b(){let e=0;for(const t of this._imageCache)e+=t.byteSize;return e}get#F(){return!(this._imageCache.size<GlobalImageCache.MIN_IMAGES_TO_CACHE)&&!(this.#b<GlobalImageCache.MAX_BYTE_SIZE)}shouldCache(e,t){let i=this._refCache.get(e);if(!i){i=new Set;this._refCache.put(e,i)}i.add(t);return!(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)&&!(!this._imageCache.has(e)&&this.#F)}addDecodeFailed(e){this.#D.put(e)}hasDecodeFailed(e){return this.#D.has(e)}addByteSize(e,t){const i=this._imageCache.get(e);i&&(i.byteSize||(i.byteSize=t))}getData(e,t){const i=this._refCache.get(e);if(!i)return null;if(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)return null;const a=this._imageCache.get(e);if(!a)return null;i.add(t);return a}setData(e,t){if(!this._refCache.has(e))throw new Error('GlobalImageCache.setData - expected "shouldCache" to have been called.');this._imageCache.has(e)||(this.#F?warn("GlobalImageCache.setData - cache limit reached."):this._imageCache.put(e,t))}clear(e=!1){if(!e){this.#D.clear();this._refCache.clear()}this._imageCache.clear()}}class PDFFunctionFactory{constructor({xref:e,isEvalSupported:t=!0}){this.xref=e;this.isEvalSupported=!1!==t}create(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parse({xref:this.xref,isEvalSupported:this.isEvalSupported,fn:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}createFromArray(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parseArray({xref:this.xref,isEvalSupported:this.isEvalSupported,fnObj:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}getCached(e){let t;e instanceof Ref?t=e:e instanceof Dict?t=e.objId:e instanceof BaseStream&&(t=e.dict?.objId);if(t){const e=this._localFunctionCache.getByRef(t);if(e)return e}return null}_cache(e,t){if(!t)throw new Error('PDFFunctionFactory._cache - expected "parsedFunction" argument.');let i;e instanceof Ref?i=e:e instanceof Dict?i=e.objId:e instanceof BaseStream&&(i=e.dict?.objId);i&&this._localFunctionCache.set(null,i,t)}get _localFunctionCache(){return shadow(this,"_localFunctionCache",new LocalFunctionCache)}}function toNumberArray(e){return Array.isArray(e)?isNumberArray(e,null)?e:e.map((e=>+e)):null}class PDFFunction{static getSampleArray(e,t,i,a){let s,r,n=1;for(s=0,r=e.length;s<r;s++)n*=e[s];n*=t;const g=new Array(n);let o=0,c=0;const C=1/(2**i-1),h=a.getBytes((n*i+7)/8);let l=0;for(s=0;s<n;s++){for(;o<i;){c<<=8;c|=h[l++];o+=8}o-=i;g[s]=(c>>o)*C;c&=(1<<o)-1}return g}static parse({xref:e,isEvalSupported:t,fn:i}){const a=i.dict||i;switch(a.get("FunctionType")){case 0:return this.constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a});case 1:break;case 2:return this.constructInterpolated({xref:e,isEvalSupported:t,dict:a});case 3:return this.constructStiched({xref:e,isEvalSupported:t,dict:a});case 4:return this.constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a})}throw new FormatError("Unknown type of function")}static parseArray({xref:e,isEvalSupported:t,fnObj:i}){if(!Array.isArray(i))return this.parse({xref:e,isEvalSupported:t,fn:i});const a=[];for(const s of i)a.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(s)}));return function(e,t,i,s){for(let r=0,n=a.length;r<n;r++)a[r](e,t,i,s+r)}}static constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a}){function toMultiArray(e){const t=e.length,i=[];let a=0;for(let s=0;s<t;s+=2)i[a++]=[e[s],e[s+1]];return i}function interpolate(e,t,i,a,s){return a+(s-a)/(i-t)*(e-t)}let s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s||!r)throw new FormatError("No domain or range");const n=s.length/2,g=r.length/2;s=toMultiArray(s);r=toMultiArray(r);const o=toNumberArray(a.getArray("Size")),c=a.get("BitsPerSample"),C=a.get("Order")||1;1!==C&&info("No support for cubic spline interpolation: "+C);let h=toNumberArray(a.getArray("Encode"));if(h)h=toMultiArray(h);else{h=[];for(let e=0;e<n;++e)h.push([0,o[e]-1])}let l=toNumberArray(a.getArray("Decode"));l=l?toMultiArray(l):r;const Q=this.getSampleArray(o,g,c,i);return function constructSampledFn(e,t,i,a){const c=1<<n,C=new Float64Array(c),E=new Uint32Array(c);let u,d;for(d=0;d<c;d++)C[d]=1;let f=g,p=1;for(u=0;u<n;++u){const i=s[u][0],a=s[u][1];let r=interpolate(Math.min(Math.max(e[t+u],i),a),i,a,h[u][0],h[u][1]);const n=o[u];r=Math.min(Math.max(r,0),n-1);const g=r<n-1?Math.floor(r):r-1,l=g+1-r,Q=r-g,m=g*f,y=m+f;for(d=0;d<c;d++)if(d&p){C[d]*=Q;E[d]+=y}else{C[d]*=l;E[d]+=m}f*=n;p<<=1}for(d=0;d<g;++d){let e=0;for(u=0;u<c;u++)e+=Q[E[u]+d]*C[u];e=interpolate(e,0,1,l[d][0],l[d][1]);i[a+d]=Math.min(Math.max(e,r[d][0]),r[d][1])}}}static constructInterpolated({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("C0"))||[0],s=toNumberArray(i.getArray("C1"))||[1],r=i.get("N"),n=[];for(let e=0,t=a.length;e<t;++e)n.push(s[e]-a[e]);const g=n.length;return function constructInterpolatedFn(e,t,i,s){const o=1===r?e[t]:e[t]**r;for(let e=0;e<g;++e)i[s+e]=a[e]+o*n[e]}}static constructStiched({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("Domain"));if(!a)throw new FormatError("No domain");if(1!==a.length/2)throw new FormatError("Bad domain for stiched function");const s=[];for(const a of i.get("Functions"))s.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(a)}));const r=toNumberArray(i.getArray("Bounds")),n=toNumberArray(i.getArray("Encode")),g=new Float32Array(1);return function constructStichedFn(e,t,i,o){const c=function constructStichedFromIRClip(e,t,i){e>i?e=i:e<t&&(e=t);return e}(e[t],a[0],a[1]),C=r.length;let h;for(h=0;h<C&&!(c<r[h]);++h);let l=a[0];h>0&&(l=r[h-1]);let Q=a[1];h<r.length&&(Q=r[h]);const E=n[2*h],u=n[2*h+1];g[0]=l===Q?E:E+(c-l)*(u-E)/(Q-l);s[h](g,0,i,o)}}static constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a}){const s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s)throw new FormatError("No domain.");if(!r)throw new FormatError("No range.");const n=new PostScriptLexer(i),g=new PostScriptParser(n).parse();if(t&&FeatureTest.isEvalSupported){const e=(new PostScriptCompiler).compile(g,s,r);if(e)return new Function("src","srcOffset","dest","destOffset",e)}info("Unable to compile PS function");const o=r.length>>1,c=s.length>>1,C=new PostScriptEvaluator(g),h=Object.create(null);let l=8192;const Q=new Float32Array(c);return function constructPostScriptFn(e,t,i,a){let s,n,g="";const E=Q;for(s=0;s<c;s++){n=e[t+s];E[s]=n;g+=n+"_"}const u=h[g];if(void 0!==u){i.set(u,a);return}const d=new Float32Array(o),f=C.execute(E),p=f.length-o;for(s=0;s<o;s++){n=f[p+s];let e=r[2*s];if(n<e)n=e;else{e=r[2*s+1];n>e&&(n=e)}d[s]=n}if(l>0){l--;h[g]=d}i.set(d,a)}}}function isPDFFunction(e){let t;if(e instanceof Dict)t=e;else{if(!(e instanceof BaseStream))return!1;t=e.dict}return t.has("FunctionType")}class PostScriptStack{static MAX_STACK_SIZE=100;constructor(e){this.stack=e?Array.from(e):[]}push(e){if(this.stack.length>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");this.stack.push(e)}pop(){if(this.stack.length<=0)throw new Error("PostScript function stack underflow.");return this.stack.pop()}copy(e){if(this.stack.length+e>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");const t=this.stack;for(let i=t.length-e,a=e-1;a>=0;a--,i++)t.push(t[i])}index(e){this.push(this.stack[this.stack.length-e-1])}roll(e,t){const i=this.stack,a=i.length-e,s=i.length-1,r=a+(t-Math.floor(t/e)*e);for(let e=a,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=a,t=r-1;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=r,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}}}class PostScriptEvaluator{constructor(e){this.operators=e}execute(e){const t=new PostScriptStack(e);let i=0;const a=this.operators,s=a.length;let r,n,g;for(;i<s;){r=a[i++];if("number"!=typeof r)switch(r){case"jz":g=t.pop();n=t.pop();n||(i=g);break;case"j":n=t.pop();i=n;break;case"abs":n=t.pop();t.push(Math.abs(n));break;case"add":g=t.pop();n=t.pop();t.push(n+g);break;case"and":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n&&g):t.push(n&g);break;case"atan":g=t.pop();n=t.pop();n=Math.atan2(n,g)/Math.PI*180;n<0&&(n+=360);t.push(n);break;case"bitshift":g=t.pop();n=t.pop();n>0?t.push(n<<g):t.push(n>>g);break;case"ceiling":n=t.pop();t.push(Math.ceil(n));break;case"copy":n=t.pop();t.copy(n);break;case"cos":n=t.pop();t.push(Math.cos(n%360/180*Math.PI));break;case"cvi":n=0|t.pop();t.push(n);break;case"cvr":break;case"div":g=t.pop();n=t.pop();t.push(n/g);break;case"dup":t.copy(1);break;case"eq":g=t.pop();n=t.pop();t.push(n===g);break;case"exch":t.roll(2,1);break;case"exp":g=t.pop();n=t.pop();t.push(n**g);break;case"false":t.push(!1);break;case"floor":n=t.pop();t.push(Math.floor(n));break;case"ge":g=t.pop();n=t.pop();t.push(n>=g);break;case"gt":g=t.pop();n=t.pop();t.push(n>g);break;case"idiv":g=t.pop();n=t.pop();t.push(n/g|0);break;case"index":n=t.pop();t.index(n);break;case"le":g=t.pop();n=t.pop();t.push(n<=g);break;case"ln":n=t.pop();t.push(Math.log(n));break;case"log":n=t.pop();t.push(Math.log10(n));break;case"lt":g=t.pop();n=t.pop();t.push(n<g);break;case"mod":g=t.pop();n=t.pop();t.push(n%g);break;case"mul":g=t.pop();n=t.pop();t.push(n*g);break;case"ne":g=t.pop();n=t.pop();t.push(n!==g);break;case"neg":n=t.pop();t.push(-n);break;case"not":n=t.pop();"boolean"==typeof n?t.push(!n):t.push(~n);break;case"or":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n||g):t.push(n|g);break;case"pop":t.pop();break;case"roll":g=t.pop();n=t.pop();t.roll(n,g);break;case"round":n=t.pop();t.push(Math.round(n));break;case"sin":n=t.pop();t.push(Math.sin(n%360/180*Math.PI));break;case"sqrt":n=t.pop();t.push(Math.sqrt(n));break;case"sub":g=t.pop();n=t.pop();t.push(n-g);break;case"true":t.push(!0);break;case"truncate":n=t.pop();n=n<0?Math.ceil(n):Math.floor(n);t.push(n);break;case"xor":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n!==g):t.push(n^g);break;default:throw new FormatError(`Unknown operator ${r}`)}else t.push(r)}return t.stack}}class AstNode{constructor(e){this.type=e}visit(e){unreachable("abstract method")}}class AstArgument extends AstNode{constructor(e,t,i){super("args");this.index=e;this.min=t;this.max=i}visit(e){e.visitArgument(this)}}class AstLiteral extends AstNode{constructor(e){super("literal");this.number=e;this.min=e;this.max=e}visit(e){e.visitLiteral(this)}}class AstBinaryOperation extends AstNode{constructor(e,t,i,a,s){super("binary");this.op=e;this.arg1=t;this.arg2=i;this.min=a;this.max=s}visit(e){e.visitBinaryOperation(this)}}class AstMin extends AstNode{constructor(e,t){super("max");this.arg=e;this.min=e.min;this.max=t}visit(e){e.visitMin(this)}}class AstVariable extends AstNode{constructor(e,t,i){super("var");this.index=e;this.min=t;this.max=i}visit(e){e.visitVariable(this)}}class AstVariableDefinition extends AstNode{constructor(e,t){super("definition");this.variable=e;this.arg=t}visit(e){e.visitVariableDefinition(this)}}class ExpressionBuilderVisitor{constructor(){this.parts=[]}visitArgument(e){this.parts.push("Math.max(",e.min,", Math.min(",e.max,", src[srcOffset + ",e.index,"]))")}visitVariable(e){this.parts.push("v",e.index)}visitLiteral(e){this.parts.push(e.number)}visitBinaryOperation(e){this.parts.push("(");e.arg1.visit(this);this.parts.push(" ",e.op," ");e.arg2.visit(this);this.parts.push(")")}visitVariableDefinition(e){this.parts.push("var ");e.variable.visit(this);this.parts.push(" = ");e.arg.visit(this);this.parts.push(";")}visitMin(e){this.parts.push("Math.min(");e.arg.visit(this);this.parts.push(", ",e.max,")")}toString(){return this.parts.join("")}}function buildAddOperation(e,t){return"literal"===t.type&&0===t.number?e:"literal"===e.type&&0===e.number?t:"literal"===t.type&&"literal"===e.type?new AstLiteral(e.number+t.number):new AstBinaryOperation("+",e,t,e.min+t.min,e.max+t.max)}function buildMulOperation(e,t){if("literal"===t.type){if(0===t.number)return new AstLiteral(0);if(1===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number*t.number)}if("literal"===e.type){if(0===e.number)return new AstLiteral(0);if(1===e.number)return t}const i=Math.min(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max),a=Math.max(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max);return new AstBinaryOperation("*",e,t,i,a)}function buildSubOperation(e,t){if("literal"===t.type){if(0===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number-t.number)}return"binary"===t.type&&"-"===t.op&&"literal"===e.type&&1===e.number&&"literal"===t.arg1.type&&1===t.arg1.number?t.arg2:new AstBinaryOperation("-",e,t,e.min-t.max,e.max-t.min)}function buildMinOperation(e,t){return e.min>=t?new AstLiteral(t):e.max<=t?e:new AstMin(e,t)}class PostScriptCompiler{compile(e,t,i){const a=[],s=[],r=t.length>>1,n=i.length>>1;let g,o,c,C,h,l,Q,E,u=0;for(let e=0;e<r;e++)a.push(new AstArgument(e,t[2*e],t[2*e+1]));for(let t=0,i=e.length;t<i;t++){E=e[t];if("number"!=typeof E)switch(E){case"add":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildAddOperation(c,C));break;case"cvr":if(a.length<1)return null;break;case"mul":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildMulOperation(c,C));break;case"sub":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildSubOperation(c,C));break;case"exch":if(a.length<2)return null;h=a.pop();l=a.pop();a.push(h,l);break;case"pop":if(a.length<1)return null;a.pop();break;case"index":if(a.length<1)return null;c=a.pop();if("literal"!==c.type)return null;g=c.number;if(g<0||!Number.isInteger(g)||a.length<g)return null;h=a[a.length-g-1];if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-g-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"dup":if(a.length<1)return null;if("number"==typeof e[t+1]&&"gt"===e[t+2]&&e[t+3]===t+7&&"jz"===e[t+4]&&"pop"===e[t+5]&&e[t+6]===e[t+1]){c=a.pop();a.push(buildMinOperation(c,e[t+1]));t+=6;break}h=a.at(-1);if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"roll":if(a.length<2)return null;C=a.pop();c=a.pop();if("literal"!==C.type||"literal"!==c.type)return null;o=C.number;g=c.number;if(g<=0||!Number.isInteger(g)||!Number.isInteger(o)||a.length<g)return null;o=(o%g+g)%g;if(0===o)break;a.push(...a.splice(a.length-g,g-o));break;default:return null}else a.push(new AstLiteral(E))}if(a.length!==n)return null;const d=[];for(const e of s){const t=new ExpressionBuilderVisitor;e.visit(t);d.push(t.toString())}for(let e=0,t=a.length;e<t;e++){const t=a[e],s=new ExpressionBuilderVisitor;t.visit(s);const r=i[2*e],n=i[2*e+1],g=[s.toString()];if(r>t.min){g.unshift("Math.max(",r,", ");g.push(")")}if(n<t.max){g.unshift("Math.min(",n,", ");g.push(")")}g.unshift("dest[destOffset + ",e,"] = ");g.push(";");d.push(g.join(""))}return d.join("\n")}}const Qs=["BN","BN","BN","BN","BN","BN","BN","BN","BN","S","B","S","WS","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","B","B","B","S","WS","ON","ON","ET","ET","ET","ON","ON","ON","ON","ON","ES","CS","ES","CS","CS","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","CS","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","BN","BN","BN","BN","BN","BN","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","CS","ON","ET","ET","ET","ET","ON","ON","ON","ON","L","ON","ON","BN","ON","ON","ET","ET","EN","EN","ON","L","ON","ON","ON","EN","L","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L"],Es=["AN","AN","AN","AN","AN","AN","ON","ON","AL","ET","ET","AL","CS","AL","ON","ON","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","AN","AN","AN","AN","AN","AN","AN","AN","AN","ET","AN","AN","AL","AL","AL","NSM","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","ON","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","NSM","NSM","ON","NSM","NSM","NSM","NSM","AL","AL","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","AL","AL","AL","AL","AL","AL"];function isOdd(e){return 0!=(1&e)}function isEven(e){return 0==(1&e)}function findUnequal(e,t,i){let a,s;for(a=t,s=e.length;a<s;++a)if(e[a]!==i)return a;return a}function setValues(e,t,i,a){for(let s=t;s<i;++s)e[s]=a}function reverseValues(e,t,i){for(let a=t,s=i-1;a<s;++a,--s){const t=e[a];e[a]=e[s];e[s]=t}}function createBidiText(e,t,i=!1){let a="ltr";i?a="ttb":t||(a="rtl");return{str:e,dir:a}}const us=[],ds=[];function bidi(e,t=-1,i=!1){let a=!0;const s=e.length;if(0===s||i)return createBidiText(e,a,i);us.length=s;ds.length=s;let r,n,g=0;for(r=0;r<s;++r){us[r]=e.charAt(r);const t=e.charCodeAt(r);let i="L";if(t<=255)i=Qs[t];else if(1424<=t&&t<=1524)i="R";else if(1536<=t&&t<=1791){i=Es[255&t];i||warn("Bidi: invalid Unicode character "+t.toString(16))}else(1792<=t&&t<=2220||64336<=t&&t<=65023||65136<=t&&t<=65279)&&(i="AL");"R"!==i&&"AL"!==i&&"AN"!==i||g++;ds[r]=i}if(0===g){a=!0;return createBidiText(e,a)}if(-1===t)if(g/s<.3&&s>4){a=!0;t=0}else{a=!1;t=1}const o=[];for(r=0;r<s;++r)o[r]=t;const c=isOdd(t)?"R":"L",C=c,h=C;let l,Q=C;for(r=0;r<s;++r)"NSM"===ds[r]?ds[r]=Q:Q=ds[r];Q=C;for(r=0;r<s;++r){l=ds[r];"EN"===l?ds[r]="AL"===Q?"AN":"EN":"R"!==l&&"L"!==l&&"AL"!==l||(Q=l)}for(r=0;r<s;++r){l=ds[r];"AL"===l&&(ds[r]="R")}for(r=1;r<s-1;++r){"ES"===ds[r]&&"EN"===ds[r-1]&&"EN"===ds[r+1]&&(ds[r]="EN");"CS"!==ds[r]||"EN"!==ds[r-1]&&"AN"!==ds[r-1]||ds[r+1]!==ds[r-1]||(ds[r]=ds[r-1])}for(r=0;r<s;++r)if("EN"===ds[r]){for(let e=r-1;e>=0&&"ET"===ds[e];--e)ds[e]="EN";for(let e=r+1;e<s&&"ET"===ds[e];++e)ds[e]="EN"}for(r=0;r<s;++r){l=ds[r];"WS"!==l&&"ES"!==l&&"ET"!==l&&"CS"!==l||(ds[r]="ON")}Q=C;for(r=0;r<s;++r){l=ds[r];"EN"===l?ds[r]="L"===Q?"L":"EN":"R"!==l&&"L"!==l||(Q=l)}for(r=0;r<s;++r)if("ON"===ds[r]){const e=findUnequal(ds,r+1,"ON");let t=C;r>0&&(t=ds[r-1]);let i=h;e+1<s&&(i=ds[e+1]);"L"!==t&&(t="R");"L"!==i&&(i="R");t===i&&setValues(ds,r,e,t);r=e-1}for(r=0;r<s;++r)"ON"===ds[r]&&(ds[r]=c);for(r=0;r<s;++r){l=ds[r];isEven(o[r])?"R"===l?o[r]+=1:"AN"!==l&&"EN"!==l||(o[r]+=2):"L"!==l&&"AN"!==l&&"EN"!==l||(o[r]+=1)}let E,u=-1,d=99;for(r=0,n=o.length;r<n;++r){E=o[r];u<E&&(u=E);d>E&&isOdd(E)&&(d=E)}for(E=u;E>=d;--E){let e=-1;for(r=0,n=o.length;r<n;++r)if(o[r]<E){if(e>=0){reverseValues(us,e,r);e=-1}}else e<0&&(e=r);e>=0&&reverseValues(us,e,o.length)}for(r=0,n=us.length;r<n;++r){const e=us[r];"<"!==e&&">"!==e||(us[r]="")}return createBidiText(us.join(""),a)}const fs={style:"normal",weight:"normal"},ps={style:"normal",weight:"bold"},ms={style:"italic",weight:"normal"},ys={style:"italic",weight:"bold"},ws=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:fs,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:ps,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roman",style:ms,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:ys,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:fs,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSans-Bold.ttf",style:ps,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:ms,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:ys,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:fs,ultimate:"monospace"}],["Courier-Bold",{alias:"Courier",style:ps,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:ms,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:ys,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:fs,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:ps,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:ms,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:ys,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:fs,fallback:"Helvetica"}],["Calibri-Bold",{alias:"Calibri",style:ps,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:ms,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:ys,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:fs}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}]]),Ds=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","light","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter((e=>!t.has(e.toLowerCase()))).join(" ")}function generateFont({alias:e,local:t,path:i,fallback:a,style:s,ultimate:r},n,g,o=!0,c=!0,C=""){const h={style:null,ultimate:null};if(t){const e=C?` ${C}`:"";for(const i of t)n.push(`local(${i}${e})`)}if(e){const t=ws.get(e),r=C||function getStyleToAppend(e){switch(e){case ps:return"Bold";case ms:return"Italic";case ys:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(s);Object.assign(h,generateFont(t,n,g,o&&!a,c&&!i,r))}s&&(h.style=s);r&&(h.ultimate=r);if(o&&a){const e=ws.get(a),{ultimate:t}=generateFont(e,n,g,o,c&&!i,C);h.ultimate||=t}c&&i&&g&&n.push(`url(${g}${i})`);return h}function getFontSubstitution(e,t,i,a,s,r){if(a.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==r&&"Type1"!==r||!/^[A-Z]{6}\+/.test(a)||(a=a.slice(7));const n=a=normalizeFontName(a);let g=e.get(n);if(g)return g;let o=ws.get(a);if(!o)for(const[e,t]of Ds)if(a.startsWith(e)){a=`${t}${a.substring(e.length)}`;o=ws.get(a);break}let c=!1;if(!o){o=ws.get(s);c=!0}const C=`${t.getDocId()}_s${t.createFontId()}`;if(!o){if(!validateFontName(a)){warn(`Cannot substitute the font because of its name: ${a}`);e.set(n,null);return null}const t=/bold/gi.test(a),i=/oblique|italic/gi.test(a),s=t&&i&&ys||t&&ps||i&&ms||fs;g={css:`"${getFamilyName(a)}",${C}`,guessFallback:!0,loadedName:C,baseFontName:a,src:`local(${a})`,style:s};e.set(n,g);return g}const h=[];c&&validateFontName(a)&&h.push(`local(${a})`);const{style:l,ultimate:Q}=generateFont(o,h,i),E=null===Q,u=E?"":`,${Q}`;g={css:`"${getFamilyName(a)}",${C}${u}`,guessFallback:E,loadedName:C,baseFontName:a,src:h.join(","),style:l};e.set(n,g);return g}class ImageResizer{constructor(e,t){this._imgData=e;this._isMask=t}static needsToBeResized(e,t){if(e<=this._goodSquareLength&&t<=this._goodSquareLength)return!1;const{MAX_DIM:i}=this;if(e>i||t>i)return!0;const a=e*t;if(this._hasMaxArea)return a>this.MAX_AREA;if(a<this._goodSquareLength**2)return!1;if(this._areGoodDims(e,t)){this._goodSquareLength=Math.max(this._goodSquareLength,Math.floor(Math.sqrt(e*t)));return!1}this._goodSquareLength=this._guessMax(this._goodSquareLength,i,128,0);return a>(this.MAX_AREA=this._goodSquareLength**2)}static get MAX_DIM(){return shadow(this,"MAX_DIM",this._guessMax(2048,65537,0,1))}static get MAX_AREA(){this._hasMaxArea=!0;return shadow(this,"MAX_AREA",this._guessMax(ImageResizer._goodSquareLength,this.MAX_DIM,128,0)**2)}static set MAX_AREA(e){if(e>=0){this._hasMaxArea=!0;shadow(this,"MAX_AREA",e)}}static setMaxArea(e){this._hasMaxArea||(this.MAX_AREA=e>>2)}static _areGoodDims(e,t){try{const i=new OffscreenCanvas(e,t),a=i.getContext("2d");a.fillRect(0,0,1,1);const s=a.getImageData(0,0,1,1).data[3];i.width=i.height=1;return 0!==s}catch{return!1}}static _guessMax(e,t,i,a){for(;e+i+1<t;){const i=Math.floor((e+t)/2),s=a||i;this._areGoodDims(i,s)?e=i:t=i}return e}static async createImage(e,t=!1){return new ImageResizer(e,t)._createImage()}async _createImage(){const e=this._encodeBMP(),t=new Blob([e.buffer],{type:"image/bmp"}),i=createImageBitmap(t),{MAX_AREA:a,MAX_DIM:s}=ImageResizer,{_imgData:r}=this,{width:n,height:g}=r,o=Math.max(n/s,g/s,Math.sqrt(n*g/a)),c=Math.max(o,2),C=Math.round(10*(o+1.25))/10/c,h=Math.floor(Math.log2(C)),l=new Array(h+2).fill(2);l[0]=c;l.splice(-1,1,C/(1<<h));let Q=n,E=g,u=await i;for(const e of l){const t=Q,i=E;Q=Math.floor(Q/e)-1;E=Math.floor(E/e)-1;const a=new OffscreenCanvas(Q,E);a.getContext("2d").drawImage(u,0,0,t,i,0,0,Q,E);u=a.transferToImageBitmap()}r.data=null;r.bitmap=u;r.width=Q;r.height=E;return r}_encodeBMP(){const{width:e,height:t,kind:i}=this._imgData;let a,s=this._imgData.data,r=new Uint8Array(0),n=r,g=0;switch(i){case b:{a=1;r=new Uint8Array(this._isMask?[255,255,255,255,0,0,0,0]:[0,0,0,0,255,255,255,255]);const i=e+7>>3,n=i+3&-4;if(i!==n){const e=new Uint8Array(n*t);let a=0;for(let r=0,g=t*i;r<g;r+=i,a+=n)e.set(s.subarray(r,r+i),a);s=e}break}case F:a=24;if(3&e){const i=3*e,a=i+3&-4,r=a-i,n=new Uint8Array(a*t);let g=0;for(let e=0,a=t*i;e<a;e+=i){const t=s.subarray(e,e+i);for(let e=0;e<i;e+=3){n[g++]=t[e+2];n[g++]=t[e+1];n[g++]=t[e]}g+=r}s=n}else for(let e=0,t=s.length;e<t;e+=3){const t=s[e];s[e]=s[e+2];s[e+2]=t}break;case S:a=32;g=3;n=new Uint8Array(68);const i=new DataView(n.buffer);if(FeatureTest.isLittleEndian){i.setUint32(0,255,!0);i.setUint32(4,65280,!0);i.setUint32(8,16711680,!0);i.setUint32(12,4278190080,!0)}else{i.setUint32(0,4278190080,!0);i.setUint32(4,16711680,!0);i.setUint32(8,65280,!0);i.setUint32(12,255,!0)}break;default:throw new Error("invalid format")}let o=0;const c=40+n.length,C=14+c+r.length+s.length,h=new Uint8Array(C),l=new DataView(h.buffer);l.setUint16(o,19778,!0);o+=2;l.setUint32(o,C,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setUint32(o,14+c+r.length,!0);o+=4;l.setUint32(o,c,!0);o+=4;l.setInt32(o,e,!0);o+=4;l.setInt32(o,-t,!0);o+=4;l.setUint16(o,1,!0);o+=2;l.setUint16(o,a,!0);o+=2;l.setUint32(o,g,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setUint32(o,r.length/4,!0);o+=4;l.setUint32(o,0,!0);o+=4;h.set(n,o);o+=n.length;h.set(r,o);o+=r.length;h.set(s,o);return h}}ImageResizer._goodSquareLength=2048;const bs=3285377520,Fs=4294901760,Ss=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:bs;this.h2=e?4294967295&e:bs}update(e){let t,i;if("string"==typeof e){t=new Uint8Array(2*e.length);i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<=255)t[i++]=s;else{t[i++]=s>>>8;t[i++]=255&s}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();i=t.byteLength}const a=i>>2,s=i-4*a,r=new Uint32Array(t.buffer,0,a);let n=0,g=0,o=this.h1,c=this.h2;const C=3432918353,h=461845907,l=11601,Q=13715;for(let e=0;e<a;e++)if(1&e){n=r[e];n=n*C&Fs|n*l&Ss;n=n<<15|n>>>17;n=n*h&Fs|n*Q&Ss;o^=n;o=o<<13|o>>>19;o=5*o+3864292196}else{g=r[e];g=g*C&Fs|g*l&Ss;g=g<<15|g>>>17;g=g*h&Fs|g*Q&Ss;c^=g;c=c<<13|c>>>19;c=5*c+3864292196}n=0;switch(s){case 3:n^=t[4*a+2]<<16;case 2:n^=t[4*a+1]<<8;case 1:n^=t[4*a];n=n*C&Fs|n*l&Ss;n=n<<15|n>>>17;n=n*h&Fs|n*Q&Ss;1&a?o^=n:c^=n}this.h1=o;this.h2=c}hexdigest(){let e=this.h1,t=this.h2;e^=t>>>1;e=3981806797*e&Fs|36045*e&Ss;t=4283543511*t&Fs|(2950163797*(t<<16|e>>>16)&Fs)>>>16;e^=t>>>1;e=444984403*e&Fs|60499*e&Ss;t=3301882366*t&Fs|(3120437893*(t<<16|e>>>16)&Fs)>>>16;e^=t>>>1;return(e>>>0).toString(16).padStart(8,"0")+(t>>>0).toString(16).padStart(8,"0")}}function addState(e,t,i,a,s){let r=e;for(let e=0,i=t.length-1;e<i;e++){const i=t[e];r=r[i]||=[]}r[t.at(-1)]={checkFn:i,iterateFn:a,processFn:s}}const ks=[];addState(ks,[xA,MA,_e,UA],null,(function iterateInlineImageGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===xA;case 1:return i[t]===MA;case 2:return i[t]===_e;case 3:return i[t]===UA}throw new Error(`iterateInlineImageGroup - invalid pos: ${a}`)}),(function foundInlineImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1,o=Math.min(Math.floor((t-r)/4),200);if(o<10)return t-(t-r)%4;let c=0;const C=[];let h=0,l=1,Q=1;for(let e=0;e<o;e++){const t=a[n+(e<<2)],i=a[g+(e<<2)][0];if(l+i.width>1e3){c=Math.max(c,l);Q+=h+2;l=0;h=0}C.push({transform:t,x:l,y:Q,w:i.width,h:i.height});l+=i.width+2;h=Math.max(h,i.height)}const E=Math.max(c,l)+1,u=Q+h+1,d=new Uint8Array(E*u*4),f=E<<2;for(let e=0;e<o;e++){const t=a[g+(e<<2)][0].data,i=C[e].w<<2;let s=0,r=C[e].x+C[e].y*E<<2;d.set(t.subarray(0,i),r-f);for(let a=0,n=C[e].h;a<n;a++){d.set(t.subarray(s,s+i),r);s+=i;r+=f}d.set(t.subarray(s-i,s),r);for(;r>=0;){t[r-4]=t[r];t[r-3]=t[r+1];t[r-2]=t[r+2];t[r-1]=t[r+3];t[r+i]=t[r+i-4];t[r+i+1]=t[r+i-3];t[r+i+2]=t[r+i-2];t[r+i+3]=t[r+i-1];r-=f}}const p={width:E,height:u};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(E,u);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(d.buffer),E,u),0,0);p.bitmap=e.transferToImageBitmap();p.data=null}else{p.kind=S;p.data=d}i.splice(r,4*o,$e);a.splice(r,4*o,[p,C]);return r+1}));addState(ks,[xA,MA,Ze,UA],null,(function iterateImageMaskGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===xA;case 1:return i[t]===MA;case 2:return i[t]===Ze;case 3:return i[t]===UA}throw new Error(`iterateImageMaskGroup - invalid pos: ${a}`)}),(function foundImageMaskGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1;let o=Math.floor((t-r)/4);if(o<10)return t-(t-r)%4;let c,C,h=!1;const l=a[g][0],Q=a[n][0],E=a[n][1],u=a[n][2],d=a[n][3];if(E===u){h=!0;c=n+4;let e=g+4;for(let t=1;t<o;t++,c+=4,e+=4){C=a[c];if(a[e][0]!==l||C[0]!==Q||C[1]!==E||C[2]!==u||C[3]!==d){t<10?h=!1:o=t;break}}}if(h){o=Math.min(o,1e3);const e=new Float32Array(2*o);c=n;for(let t=0;t<o;t++,c+=4){C=a[c];e[t<<1]=C[4];e[1+(t<<1)]=C[5]}i.splice(r,4*o,et);a.splice(r,4*o,[l,Q,E,u,d,e])}else{o=Math.min(o,100);const e=[];for(let t=0;t<o;t++){C=a[n+(t<<2)];const i=a[g+(t<<2)][0];e.push({data:i.data,width:i.width,height:i.height,interpolate:i.interpolate,count:i.count,transform:C})}i.splice(r,4*o,Ve);a.splice(r,4*o,[e])}return r+1}));addState(ks,[xA,MA,ze,UA],(function(e){const t=e.argsArray,i=e.iCurr-2;return 0===t[i][1]&&0===t[i][2]}),(function iterateImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-3))%4;switch(s){case 0:return i[t]===xA;case 1:if(i[t]!==MA)return!1;const s=e.iCurr-2,r=a[s][0],n=a[s][3];return a[t][0]===r&&0===a[t][1]&&0===a[t][2]&&a[t][3]===n;case 2:if(i[t]!==ze)return!1;const g=a[e.iCurr-1][0];return a[t][0]===g;case 3:return i[t]===UA}throw new Error(`iterateImageGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=a[s-1][0],o=a[n][0],c=a[n][3],C=Math.min(Math.floor((t-r)/4),1e3);if(C<3)return t-(t-r)%4;const h=new Float32Array(2*C);let l=n;for(let e=0;e<C;e++,l+=4){const t=a[l];h[e<<1]=t[4];h[1+(e<<1)]=t[5]}const Q=[g,o,c,h];i.splice(r,4*C,At);a.splice(r,4*C,Q);return r+1}));addState(ks,[Ae,re,ce,he,ee],null,(function iterateShowTextGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-4))%5;switch(s){case 0:return i[t]===Ae;case 1:return i[t]===re;case 2:return i[t]===ce;case 3:if(i[t]!==he)return!1;const s=e.iCurr-3,r=a[s][0],n=a[s][1];return a[t][0]===r&&a[t][1]===n;case 4:return i[t]===ee}throw new Error(`iterateShowTextGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-4,n=s-3,g=s-2,o=s-1,c=s,C=a[n][0],h=a[n][1];let l=Math.min(Math.floor((t-r)/5),1e3);if(l<3)return t-(t-r)%5;let Q=r;if(r>=4&&i[r-4]===i[n]&&i[r-3]===i[g]&&i[r-2]===i[o]&&i[r-1]===i[c]&&a[r-4][0]===C&&a[r-4][1]===h){l++;Q-=5}let E=Q+4;for(let e=1;e<l;e++){i.splice(E,3);a.splice(E,3);E+=2}return E+1}));class NullOptimizer{constructor(e){this.queue=e}_optimize(){}push(e,t){this.queue.fnArray.push(e);this.queue.argsArray.push(t);this._optimize()}flush(){}reset(){}}class QueueOptimizer extends NullOptimizer{constructor(e){super(e);this.state=null;this.context={iCurr:0,fnArray:e.fnArray,argsArray:e.argsArray,isOffscreenCanvasSupported:!1};this.match=null;this.lastProcessed=0}set isOffscreenCanvasSupported(e){this.context.isOffscreenCanvasSupported=e}_optimize(){const e=this.queue.fnArray;let t=this.lastProcessed,i=e.length,a=this.state,s=this.match;if(!a&&!s&&t+1===i&&!ks[e[t]]){this.lastProcessed=i;return}const r=this.context;for(;t<i;){if(s){if((0,s.iterateFn)(r,t)){t++;continue}t=(0,s.processFn)(r,t+1);i=e.length;s=null;a=null;if(t>=i)break}a=(a||ks)[e[t]];if(a&&!Array.isArray(a)){r.iCurr=t;t++;if(!a.checkFn||(0,a.checkFn)(r)){s=a;a=null}else a=null}else t++}this.state=a;this.match=s;this.lastProcessed=t}flush(){for(;this.match;){const e=this.queue.fnArray.length;this.lastProcessed=(0,this.match.processFn)(this.context,e);this.match=null;this.state=null;this._optimize()}}reset(){this.state=null;this.match=null;this.lastProcessed=0}}class OperatorList{static CHUNK_SIZE=1e3;static CHUNK_SIZE_ABOUT=this.CHUNK_SIZE-5;constructor(e=0,t){this._streamSink=t;this.fnArray=[];this.argsArray=[];this.optimizer=!t||e&E?new NullOptimizer(this):new QueueOptimizer(this);this.dependencies=new Set;this._totalLength=0;this.weight=0;this._resolved=t?null:Promise.resolve()}set isOffscreenCanvasSupported(e){this.optimizer.isOffscreenCanvasSupported=e}get length(){return this.argsArray.length}get ready(){return this._resolved||this._streamSink.ready}get totalLength(){return this._totalLength+this.length}addOp(e,t){this.optimizer.push(e,t);this.weight++;this._streamSink&&(this.weight>=OperatorList.CHUNK_SIZE||this.weight>=OperatorList.CHUNK_SIZE_ABOUT&&(e===UA||e===ee))&&this.flush()}addImageOps(e,t,i){void 0!==i&&this.addOp(Ye,["OC",i]);this.addOp(e,t);void 0!==i&&this.addOp(ve,[])}addDependency(e){if(!this.dependencies.has(e)){this.dependencies.add(e);this.addOp(wA,[e])}}addDependencies(e){for(const t of e)this.addDependency(t)}addOpList(e){if(e instanceof OperatorList){for(const t of e.dependencies)this.dependencies.add(t);for(let t=0,i=e.length;t<i;t++)this.addOp(e.fnArray[t],e.argsArray[t])}else warn('addOpList - ignoring invalid "opList" parameter.')}getIR(){return{fnArray:this.fnArray,argsArray:this.argsArray,length:this.length}}get _transfers(){const e=[],{fnArray:t,argsArray:i,length:a}=this;for(let s=0;s<a;s++)switch(t[s]){case _e:case $e:case Ze:const t=i[s][0];!t.cached&&t.data?.buffer instanceof ArrayBuffer&&e.push(t.data.buffer)}return e}flush(e=!1,t=null){this.optimizer.flush();const i=this.length;this._totalLength+=i;this._streamSink.enqueue({fnArray:this.fnArray,argsArray:this.argsArray,lastChunk:e,separateAnnots:t,length:i},1,this._transfers);this.dependencies.clear();this.fnArray.length=0;this.argsArray.length=0;this.weight=0;this.optimizer.reset()}}function decodeAndClamp(e,t,i,a){(e=t+e*i)<0?e=0:e>a&&(e=a);return e}function resizeImageMask(e,t,i,a,s,r){const n=s*r;let g;g=t<=8?new Uint8Array(n):t<=16?new Uint16Array(n):new Uint32Array(n);const o=i/s,c=a/r;let C,h,l,Q,E=0;const u=new Uint16Array(s),d=i;for(C=0;C<s;C++)u[C]=Math.floor(C*o);for(C=0;C<r;C++){l=Math.floor(C*c)*d;for(h=0;h<s;h++){Q=l+u[h];g[E++]=e[Q]}}return g}class PDFImage{constructor({xref:e,res:t,image:i,isInline:a=!1,smask:s=null,mask:r=null,isMask:n=!1,pdfFunctionFactory:g,localColorSpaceCache:o}){this.image=i;const c=i.dict,C=c.get("F","Filter");let h;if(C instanceof Name)h=C.name;else if(Array.isArray(C)){const t=e.fetchIfRef(C[0]);t instanceof Name&&(h=t.name)}switch(h){case"JPXDecode":({width:i.width,height:i.height,componentsCount:i.numComps,bitsPerComponent:i.bitsPerComponent}=JpxImage.parseImageProperties(i.stream));i.stream.reset();this.jpxDecoderOptions={numComponents:0,isIndexedColormap:!1,smaskInData:c.has("SMaskInData")};break;case"JBIG2Decode":i.bitsPerComponent=1;i.numComps=1}let l=c.get("W","Width"),Q=c.get("H","Height");if(Number.isInteger(i.width)&&i.width>0&&Number.isInteger(i.height)&&i.height>0&&(i.width!==l||i.height!==Q)){warn("PDFImage - using the Width/Height of the image data, rather than the image dictionary.");l=i.width;Q=i.height}if(l<1||Q<1)throw new FormatError(`Invalid image width: ${l} or height: ${Q}`);this.width=l;this.height=Q;this.interpolate=c.get("I","Interpolate");this.imageMask=c.get("IM","ImageMask")||!1;this.matte=c.get("Matte")||!1;let E=i.bitsPerComponent;if(!E){E=c.get("BPC","BitsPerComponent");if(!E){if(!this.imageMask)throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);E=1}}this.bpc=E;if(!this.imageMask){let s=c.getRaw("CS")||c.getRaw("ColorSpace");const r=!!s;if(r)this.jpxDecoderOptions?.smaskInData&&(s=Name.get("DeviceRGBA"));else if(this.jpxDecoderOptions)s=Name.get("DeviceRGBA");else switch(i.numComps){case 1:s=Name.get("DeviceGray");break;case 3:s=Name.get("DeviceRGB");break;case 4:s=Name.get("DeviceCMYK");break;default:throw new Error(`Images with ${i.numComps} color components not supported.`)}this.colorSpace=ColorSpace.parse({cs:s,xref:e,resources:a?t:null,pdfFunctionFactory:g,localColorSpaceCache:o});this.numComps=this.colorSpace.numComps;if(this.jpxDecoderOptions){this.jpxDecoderOptions.numComponents=r?this.numComp:0;this.jpxDecoderOptions.isIndexedColormap="Indexed"===this.colorSpace.name}}this.decode=c.getArray("D","Decode");this.needsDecode=!1;if(this.decode&&(this.colorSpace&&!this.colorSpace.isDefaultDecode(this.decode,E)||n&&!ColorSpace.isDefaultDecode(this.decode,1))){this.needsDecode=!0;const e=(1<<E)-1;this.decodeCoefficients=[];this.decodeAddends=[];const t="Indexed"===this.colorSpace?.name;for(let i=0,a=0;i<this.decode.length;i+=2,++a){const s=this.decode[i],r=this.decode[i+1];this.decodeCoefficients[a]=t?(r-s)/e:r-s;this.decodeAddends[a]=t?s:e*s}}if(s)this.smask=new PDFImage({xref:e,res:t,image:s,isInline:a,pdfFunctionFactory:g,localColorSpaceCache:o});else if(r)if(r instanceof BaseStream){r.dict.get("IM","ImageMask")?this.mask=new PDFImage({xref:e,res:t,image:r,isInline:a,isMask:!0,pdfFunctionFactory:g,localColorSpaceCache:o}):warn("Ignoring /Mask in image without /ImageMask.")}else this.mask=r}static async buildImage({xref:e,res:t,image:i,isInline:a=!1,pdfFunctionFactory:s,localColorSpaceCache:r}){const n=i;let g=null,o=null;const c=i.dict.get("SMask"),C=i.dict.get("Mask");c?c instanceof BaseStream?g=c:warn("Unsupported /SMask format."):C&&(C instanceof BaseStream||Array.isArray(C)?o=C:warn("Unsupported /Mask format."));return new PDFImage({xref:e,res:t,image:n,isInline:a,smask:g,mask:o,pdfFunctionFactory:s,localColorSpaceCache:r})}static createRawMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r}){const n=(t+7>>3)*i,g=e.byteLength;let o,c;if(!a||s&&!(n===g))if(s){o=new Uint8Array(n);o.set(e);o.fill(255,g)}else o=new Uint8Array(e);else o=e;if(s)for(c=0;c<g;c++)o[c]^=255;return{data:o,width:t,height:i,interpolate:r}}static async createMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r,isOffscreenCanvasSupported:n=!1}){const g=1===t&&1===i&&s===(0===e.length||!!(128&e[0]));if(g)return{isSingleOpaquePixel:g};if(n){if(ImageResizer.needsToBeResized(t,i)){const a=new Uint8ClampedArray(t*i*4);convertBlackAndWhiteToRGBA({src:e,dest:a,width:t,height:i,nonBlackColor:0,inverseDecode:s});return ImageResizer.createImage({kind:S,data:a,width:t,height:i,interpolate:r})}const a=new OffscreenCanvas(t,i),n=a.getContext("2d"),g=n.createImageData(t,i);convertBlackAndWhiteToRGBA({src:e,dest:g.data,width:t,height:i,nonBlackColor:0,inverseDecode:s});n.putImageData(g,0,0);return{data:null,width:t,height:i,interpolate:r,bitmap:a.transferToImageBitmap()}}return this.createRawMask({imgArray:e,width:t,height:i,inverseDecode:s,imageIsFromDecodeStream:a,interpolate:r})}get drawWidth(){return Math.max(this.width,this.smask?.width||0,this.mask?.width||0)}get drawHeight(){return Math.max(this.height,this.smask?.height||0,this.mask?.height||0)}decodeBuffer(e){const t=this.bpc,i=this.numComps,a=this.decodeAddends,s=this.decodeCoefficients,r=(1<<t)-1;let n,g;if(1===t){for(n=0,g=e.length;n<g;n++)e[n]=+!e[n];return}let o=0;for(n=0,g=this.width*this.height;n<g;n++)for(let t=0;t<i;t++){e[o]=decodeAndClamp(e[o],a[t],s[t],r);o++}}getComponents(e){const t=this.bpc;if(8===t)return e;const i=this.width,a=this.height,s=this.numComps,r=i*a*s;let n,g=0;n=t<=8?new Uint8Array(r):t<=16?new Uint16Array(r):new Uint32Array(r);const o=i*s,c=(1<<t)-1;let C,h,l=0;if(1===t){let t,i,s;for(let r=0;r<a;r++){i=l+(-8&o);s=l+o;for(;l<i;){h=e[g++];n[l]=h>>7&1;n[l+1]=h>>6&1;n[l+2]=h>>5&1;n[l+3]=h>>4&1;n[l+4]=h>>3&1;n[l+5]=h>>2&1;n[l+6]=h>>1&1;n[l+7]=1&h;l+=8}if(l<s){h=e[g++];t=128;for(;l<s;){n[l++]=+!!(h&t);t>>=1}}}}else{let i=0;h=0;for(l=0,C=r;l<C;++l){if(l%o==0){h=0;i=0}for(;i<t;){h=h<<8|e[g++];i+=8}const a=i-t;let s=h>>a;s<0?s=0:s>c&&(s=c);n[l]=s;h&=(1<<a)-1;i=a}}return n}async fillOpacity(e,t,i,a,s){const r=this.smask,n=this.mask;let g,o,c,C,h,l;if(r){o=r.width;c=r.height;g=new Uint8ClampedArray(o*c);await r.fillGrayBuffer(g);o===t&&c===i||(g=resizeImageMask(g,r.bpc,o,c,t,i))}else if(n)if(n instanceof PDFImage){o=n.width;c=n.height;g=new Uint8ClampedArray(o*c);n.numComps=1;await n.fillGrayBuffer(g);for(C=0,h=o*c;C<h;++C)g[C]=255-g[C];o===t&&c===i||(g=resizeImageMask(g,n.bpc,o,c,t,i))}else{if(!Array.isArray(n))throw new FormatError("Unknown mask format.");{g=new Uint8ClampedArray(t*i);const e=this.numComps;for(C=0,h=t*i;C<h;++C){let t=0;const i=C*e;for(l=0;l<e;++l){const e=s[i+l],a=2*l;if(e<n[a]||e>n[a+1]){t=255;break}}g[C]=t}}}if(g)for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=g[C];else for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=255}undoPreblend(e,t,i){const a=this.smask?.matte;if(!a)return;const s=this.colorSpace.getRgb(a,0),r=s[0],n=s[1],g=s[2],o=t*i*4;for(let t=0;t<o;t+=4){const i=e[t+3];if(0===i){e[t]=255;e[t+1]=255;e[t+2]=255;continue}const a=255/i;e[t]=(e[t]-r)*a+r;e[t+1]=(e[t+1]-n)*a+n;e[t+2]=(e[t+2]-g)*a+g}}async createImageData(e=!1,t=!1){const i=this.drawWidth,a=this.drawHeight,s={width:i,height:a,interpolate:this.interpolate,kind:0,data:null},r=this.numComps,n=this.width,g=this.height,o=this.bpc,c=n*r*o+7>>3,C=t&&ImageResizer.needsToBeResized(i,a);if("DeviceRGBA"===this.colorSpace.name){s.kind=S;const e=s.data=await this.getImageBytes(g*n*4,{});return t?C?ImageResizer.createImage(s,!1):this.createBitmap(S,i,a,e):s}if(!e){let e;"DeviceGray"===this.colorSpace.name&&1===o?e=b:"DeviceRGB"!==this.colorSpace.name||8!==o||this.needsDecode||(e=F);if(e&&!this.smask&&!this.mask&&i===n&&a===g){const r=await this.getImageBytes(g*c,{});if(t)return C?ImageResizer.createImage({data:r,kind:e,width:i,height:a,interpolate:this.interpolate},this.needsDecode):this.createBitmap(e,n,g,r);s.kind=e;s.data=r;if(this.needsDecode){assert(e===b,"PDFImage.createImageData: The image must be grayscale.");const t=s.data;for(let e=0,i=t.length;e<i;e++)t[e]^=255}return s}if(this.image instanceof JpegStream&&!this.smask&&!this.mask&&!this.needsDecode){let e=g*c;if(t&&!C){let t=!1;switch(this.colorSpace.name){case"DeviceGray":e*=4;t=!0;break;case"DeviceRGB":e=e/3*4;t=!0;break;case"DeviceCMYK":t=!0}if(t){const t=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGBA:!0});return this.createBitmap(S,i,a,t)}}else switch(this.colorSpace.name){case"DeviceGray":e*=3;case"DeviceRGB":case"DeviceCMYK":s.kind=F;s.data=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGB:!0});return C?ImageResizer.createImage(s):s}}}const h=await this.getImageBytes(g*c,{internal:!0}),l=0|h.length/c*a/g,Q=this.getComponents(h);let E,u,d,f,p,m;if(t&&!C){d=new OffscreenCanvas(i,a);f=d.getContext("2d");p=f.createImageData(i,a);m=p.data}s.kind=S;if(e||this.smask||this.mask){t&&!C||(m=new Uint8ClampedArray(i*a*4));E=1;u=!0;await this.fillOpacity(m,i,a,l,Q)}else{if(!t||C){s.kind=F;m=new Uint8ClampedArray(i*a*3);E=0}else{new Uint32Array(m.buffer).fill(FeatureTest.isLittleEndian?4278190080:255);E=1}u=!1}this.needsDecode&&this.decodeBuffer(Q);this.colorSpace.fillRgb(m,n,g,i,a,l,o,Q,E);u&&this.undoPreblend(m,i,l);if(t&&!C){f.putImageData(p,0,0);return{data:null,width:i,height:a,bitmap:d.transferToImageBitmap(),interpolate:this.interpolate}}s.data=m;return C?ImageResizer.createImage(s):s}async fillGrayBuffer(e){const t=this.numComps;if(1!==t)throw new FormatError(`Reading gray scale from a color image: ${t}`);const i=this.width,a=this.height,s=this.bpc,r=i*t*s+7>>3,n=await this.getImageBytes(a*r,{internal:!0}),g=this.getComponents(n);let o,c;if(1===s){c=i*a;if(this.needsDecode)for(o=0;o<c;++o)e[o]=g[o]-1&255;else for(o=0;o<c;++o)e[o]=255&-g[o];return}this.needsDecode&&this.decodeBuffer(g);c=i*a;const C=255/((1<<s)-1);for(o=0;o<c;++o)e[o]=C*g[o]}createBitmap(e,t,i,a){const s=new OffscreenCanvas(t,i),r=s.getContext("2d");let n;if(e===S)n=new ImageData(a,t,i);else{n=r.createImageData(t,i);convertToRGBA({kind:e,src:a,dest:new Uint32Array(n.data.buffer),width:t,height:i,inverseDecode:this.needsDecode})}r.putImageData(n,0,0);return{data:null,width:t,height:i,bitmap:s.transferToImageBitmap(),interpolate:this.interpolate}}async getImageBytes(e,{drawWidth:t,drawHeight:i,forceRGBA:a=!1,forceRGB:s=!1,internal:r=!1}){this.image.reset();this.image.drawWidth=t||this.width;this.image.drawHeight=i||this.height;this.image.forceRGBA=!!a;this.image.forceRGB=!!s;const n=await this.image.getImageData(e,this.jpxDecoderOptions);if(r||this.image instanceof DecodeStream)return n;assert(n instanceof Uint8Array,'PDFImage.getImageBytes: Unsupported "imageBytes" type.');return new Uint8Array(n)}}const Rs=Object.freeze({maxImageSize:-1,disableFontFace:!1,ignoreErrors:!1,isEvalSupported:!0,isOffscreenCanvasSupported:!1,canvasMaxAreaInBytes:-1,fontExtraProperties:!1,useSystemFonts:!0,cMapUrl:null,standardFontDataUrl:null}),Ns=1,Gs=2,xs=Promise.resolve();function normalizeBlendMode(e,t=!1){if(Array.isArray(e)){for(const t of e){const e=normalizeBlendMode(t,!0);if(e)return e}warn(`Unsupported blend mode Array: ${e}`);return"source-over"}if(!(e instanceof Name))return t?null:"source-over";switch(e.name){case"Normal":case"Compatible":return"source-over";case"Multiply":return"multiply";case"Screen":return"screen";case"Overlay":return"overlay";case"Darken":return"darken";case"Lighten":return"lighten";case"ColorDodge":return"color-dodge";case"ColorBurn":return"color-burn";case"HardLight":return"hard-light";case"SoftLight":return"soft-light";case"Difference":return"difference";case"Exclusion":return"exclusion";case"Hue":return"hue";case"Saturation":return"saturation";case"Color":return"color";case"Luminosity":return"luminosity"}if(t)return null;warn(`Unsupported blend mode: ${e.name}`);return"source-over"}function addLocallyCachedImageOps(e,t){t.objId&&e.addDependency(t.objId);e.addImageOps(t.fn,t.args,t.optionalContent);t.fn===Ze&&t.args[0]?.count>0&&t.args[0].count++}class TimeSlotManager{static TIME_SLOT_DURATION_MS=20;static CHECK_TIME_EVERY=100;constructor(){this.reset()}check(){if(++this.checked<TimeSlotManager.CHECK_TIME_EVERY)return!1;this.checked=0;return this.endTime<=Date.now()}reset(){this.endTime=Date.now()+TimeSlotManager.TIME_SLOT_DURATION_MS;this.checked=0}}class PartialEvaluator{constructor({xref:e,handler:t,pageIndex:i,idFactory:a,fontCache:s,builtInCMapCache:r,standardFontDataCache:n,globalImageCache:g,systemFontCache:o,options:c=null}){this.xref=e;this.handler=t;this.pageIndex=i;this.idFactory=a;this.fontCache=s;this.builtInCMapCache=r;this.standardFontDataCache=n;this.globalImageCache=g;this.systemFontCache=o;this.options=c||Rs;this.type3FontRefs=null;this._regionalImageCache=new RegionalImageCache;this._fetchBuiltInCMapBound=this.fetchBuiltInCMap.bind(this);ImageResizer.setMaxArea(this.options.canvasMaxAreaInBytes)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.options.isEvalSupported}))}get parsingType3Font(){return!!this.type3FontRefs}clone(e=null){const t=Object.create(this);t.options=Object.assign(Object.create(null),this.options,e);return t}hasBlendModes(e,t){if(!(e instanceof Dict))return!1;if(e.objId&&t.has(e.objId))return!1;const i=new RefSet(t);e.objId&&i.put(e.objId);const a=[e],s=this.xref;for(;a.length;){const e=a.shift(),t=e.get("ExtGState");if(t instanceof Dict)for(let e of t.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring ExtGState: "${t}".`);continue}}if(!(e instanceof Dict))continue;e.objId&&i.put(e.objId);const t=e.get("BM");if(t instanceof Name){if("Normal"!==t.name)return!0}else if(void 0!==t&&Array.isArray(t))for(const e of t)if(e instanceof Name&&"Normal"!==e.name)return!0}const r=e.get("XObject");if(r instanceof Dict)for(let e of r.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring XObject: "${t}".`);continue}}if(!(e instanceof BaseStream))continue;e.dict.objId&&i.put(e.dict.objId);const t=e.dict.get("Resources");if(t instanceof Dict&&(!t.objId||!i.has(t.objId))){a.push(t);t.objId&&i.put(t.objId)}}}for(const e of i)t.put(e);return!1}async fetchBuiltInCMap(e){const t=this.builtInCMapCache.get(e);if(t)return t;let i;if(null!==this.options.cMapUrl){const t=`${this.options.cMapUrl}${e}.bcmap`,a=await fetch(t);if(!a.ok)throw new Error(`fetchBuiltInCMap: failed to fetch file "${t}" with "${a.statusText}".`);i={cMapData:new Uint8Array(await a.arrayBuffer()),compressionType:yA.BINARY}}else i=await this.handler.sendWithPromise("FetchBuiltInCMap",{name:e});i.compressionType!==yA.NONE&&this.builtInCMapCache.set(e,i);return i}async fetchStandardFontData(e){const t=this.standardFontDataCache.get(e);if(t)return new Stream(t);if(this.options.useSystemFonts&&"Symbol"!==e&&"ZapfDingbats"!==e)return null;const i=Vi()[e];let a;if(null!==this.options.standardFontDataUrl){const e=`${this.options.standardFontDataUrl}${i}`,t=await fetch(e);t.ok?a=new Uint8Array(await t.arrayBuffer()):warn(`fetchStandardFontData: failed to fetch file "${e}" with "${t.statusText}".`)}else try{a=await this.handler.sendWithPromise("FetchStandardFontData",{filename:i})}catch(e){warn(`fetchStandardFontData: failed to fetch file "${i}" with "${e}".`)}if(!a)return null;this.standardFontDataCache.set(e,a);return new Stream(a)}async buildFormXObject(e,t,i,a,s,r,n){const g=t.dict,o=lookupMatrix(g.getArray("Matrix"),null),c=lookupNormalRect(g.getArray("BBox"),null);let C,h;g.has("OC")&&(C=await this.parseMarkedContentProps(g.get("OC"),e));void 0!==C&&a.addOp(Ye,["OC",C]);const l=g.get("Group");if(l){h={matrix:o,bbox:c,smask:i,isolated:!1,knockout:!1};let t=null;if(isName(l.get("S"),"Transparency")){h.isolated=l.get("I")||!1;h.knockout=l.get("K")||!1;if(l.has("CS")){const i=l.getRaw("CS"),a=ColorSpace.getCached(i,this.xref,n);t=a||await this.parseColorSpace({cs:i,resources:e,localColorSpaceCache:n})}}if(i?.backdrop){t||=ColorSpace.singletons.rgb;i.backdrop=t.getRgb(i.backdrop,0)}a.addOp(Pe,[h])}const Q=l?[o,null]:[o,c];a.addOp(qe,Q);await this.getOperatorList({stream:t,task:s,resources:g.get("Resources")||e,operatorList:a,initialState:r});a.addOp(Oe,[]);l&&a.addOp(We,[h]);void 0!==C&&a.addOp(ve,[])}_sendImgData(e,t,i=!1){const a=t?[t.bitmap||t.data.buffer]:null;return this.parsingType3Font||i?this.handler.send("commonobj",[e,"Image",t],a):this.handler.send("obj",[e,this.pageIndex,"Image",t],a)}async buildPaintImageXObject({resources:e,image:t,isInline:i=!1,operatorList:a,cacheKey:s,localImageCache:r,localColorSpaceCache:n}){const g=t.dict,o=g.objId,c=g.get("W","Width"),C=g.get("H","Height");if(!c||"number"!=typeof c||!C||"number"!=typeof C){warn("Image dimensions are missing, or not numbers.");return}const h=this.options.maxImageSize;if(-1!==h&&c*C>h){const e="Image exceeded maximum allowed size and was removed.";if(this.options.ignoreErrors){warn(e);return}throw new Error(e)}let l;g.has("OC")&&(l=await this.parseMarkedContentProps(g.get("OC"),e));let Q,E;if(g.get("IM","ImageMask")||!1){const e=g.get("I","Interpolate"),i=c+7>>3,n=t.getBytes(i*C),h=g.getArray("D","Decode");if(this.parsingType3Font){Q=PDFImage.createRawMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e});Q.cached=!!s;E=[Q];a.addImageOps(Ze,E,l);if(s){const e={fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}Q=await PDFImage.createMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e,isOffscreenCanvasSupported:this.options.isOffscreenCanvasSupported});if(Q.isSingleOpaquePixel){a.addImageOps(tt,[],l);if(s){const e={fn:tt,args:[],optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}const u=`mask_${this.idFactory.createObjId()}`;a.addDependency(u);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;this._sendImgData(u,Q);E=[{data:u,width:Q.width,height:Q.height,interpolate:Q.interpolate,count:1}];a.addImageOps(Ze,E,l);if(s){const e={objId:u,fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}if(i&&c+C<200&&!g.has("SMask")&&!g.has("Mask")){try{const s=new PDFImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n});Q=await s.createImageData(!0,!1);a.isOffscreenCanvasSupported=this.options.isOffscreenCanvasSupported;a.addImageOps(_e,[Q],l)}catch(e){const t=`Unable to decode inline image: "${e}".`;if(!this.options.ignoreErrors)throw new Error(t);warn(t)}return}let u=`img_${this.idFactory.createObjId()}`,d=!1;if(this.parsingType3Font)u=`${this.idFactory.getDocId()}_type3_${u}`;else if(s&&o){d=this.globalImageCache.shouldCache(o,this.pageIndex);if(d){assert(!i,"Cannot cache an inline image globally.");u=`${this.idFactory.getDocId()}_${u}`}}a.addDependency(u);E=[u,c,C];a.addImageOps(ze,E,l);if(d){if(this.globalImageCache.hasDecodeFailed(o)){this.globalImageCache.setData(o,{objId:u,fn:ze,args:E,optionalContent:l,byteSize:0});this._sendImgData(u,null,d);return}if(c*C>25e4||g.has("SMask")||g.has("Mask")){const e=await this.handler.sendWithPromise("commonobj",[u,"CopyLocalImage",{imageRef:o}]);if(e){this.globalImageCache.setData(o,{objId:u,fn:ze,args:E,optionalContent:l,byteSize:0});this.globalImageCache.addByteSize(o,e);return}}}PDFImage.buildImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n}).then((async e=>{Q=await e.createImageData(!1,this.options.isOffscreenCanvasSupported);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;Q.ref=o;d&&this.globalImageCache.addByteSize(o,Q.dataLen);return this._sendImgData(u,Q,d)})).catch((e=>{warn(`Unable to decode image "${u}": "${e}".`);o&&this.globalImageCache.addDecodeFailed(o);return this._sendImgData(u,null,d)}));if(s){const e={objId:u,fn:ze,args:E,optionalContent:l};r.set(s,o,e);if(o){this._regionalImageCache.set(null,o,e);d&&this.globalImageCache.setData(o,{objId:u,fn:ze,args:E,optionalContent:l,byteSize:0})}}}handleSMask(e,t,i,a,s,r){const n=e.get("G"),g={subtype:e.get("S").name,backdrop:e.get("BC")},o=e.get("TR");if(isPDFFunction(o)){const e=this._pdfFunctionFactory.create(o),t=new Uint8Array(256),i=new Float32Array(1);for(let a=0;a<256;a++){i[0]=a/255;e(i,0,i,0);t[a]=255*i[0]|0}g.transferMap=t}return this.buildFormXObject(t,n,g,i,a,s.state.clone(),r)}handleTransferFunction(e){let t;if(Array.isArray(e))t=e;else{if(!isPDFFunction(e))return null;t=[e]}const i=[];let a=0,s=0;for(const e of t){const t=this.xref.fetchIfRef(e);a++;if(isName(t,"Identity")){i.push(null);continue}if(!isPDFFunction(t))return null;const r=this._pdfFunctionFactory.create(t),n=new Uint8Array(256),g=new Float32Array(1);for(let e=0;e<256;e++){g[0]=e/255;r(g,0,g,0);n[e]=255*g[0]|0}i.push(n);s++}return 1!==a&&4!==a||0===s?null:i}handleTilingType(e,t,i,a,s,r,n,g){const o=new OperatorList,c=Dict.merge({xref:this.xref,dictArray:[s.get("Resources"),i]});return this.getOperatorList({stream:a,task:n,resources:c,operatorList:o}).then((function(){const i=o.getIR(),a=getTilingPatternIR(i,s,t);r.addDependencies(o.dependencies);r.addOp(e,a);s.objId&&g.set(null,s.objId,{operatorListIR:i,dict:s})})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`handleTilingType - ignoring pattern: "${e}".`)}}))}async handleSetFont(e,t,i,a,s,r,n=null,g=null){const o=t?.[0]instanceof Name?t[0].name:null;let c=await this.loadFont(o,i,e,n,g);if(c.font.isType3Font)try{await c.loadType3Data(this,e,s);a.addDependencies(c.type3Dependencies)}catch(e){c=new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Type3 font load error: ${e}`),dict:c.font,evaluatorOptions:this.options})}r.font=c.font;c.send(this.handler);return c.loadedName}handleText(e,t){const i=t.font,a=i.charsToGlyphs(e);if(i.data){(!!(t.textRenderingMode&D)||"Pattern"===t.fillColorSpace.name||i.disableFontFace||this.options.disableFontFace)&&PartialEvaluator.buildFontPaths(i,a,this.handler,this.options)}return a}ensureStateFont(e){if(e.font)return;const t=new FormatError("Missing setFont (Tf) operator before text rendering operator.");if(!this.options.ignoreErrors)throw t;warn(`ensureStateFont: "${t}".`)}async setGState({resources:e,gState:t,operatorList:i,cacheKey:a,task:s,stateManager:r,localGStateCache:n,localColorSpaceCache:g}){const o=t.objId;let c=!0;const C=[];let h=Promise.resolve();for(const a of t.getKeys()){const n=t.get(a);switch(a){case"Type":break;case"LW":case"LC":case"LJ":case"ML":case"D":case"RI":case"FL":case"CA":case"ca":C.push([a,n]);break;case"Font":c=!1;h=h.then((()=>this.handleSetFont(e,null,n[0],i,s,r.state).then((function(e){i.addDependency(e);C.push([a,[e,n[1]]])}))));break;case"BM":C.push([a,normalizeBlendMode(n)]);break;case"SMask":if(isName(n,"None")){C.push([a,!1]);break}if(n instanceof Dict){c=!1;h=h.then((()=>this.handleSMask(n,e,i,s,r,g)));C.push([a,!0])}else warn("Unsupported SMask type");break;case"TR":const t=this.handleTransferFunction(n);C.push([a,t]);break;case"OP":case"op":case"OPM":case"BG":case"BG2":case"UCR":case"UCR2":case"TR2":case"HT":case"SM":case"SA":case"AIS":case"TK":info("graphic state operator "+a);break;default:info("Unknown graphic state operator "+a)}}await h;C.length>0&&i.addOp(GA,[C]);c&&n.set(a,o,C)}loadFont(e,t,i,a=null,s=null){const errorFont=async()=>new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Font "${e}" is not available.`),dict:t,evaluatorOptions:this.options});let r;if(t)t instanceof Ref&&(r=t);else{const t=i.get("Font");t&&(r=t.getRaw(e))}if(r){if(this.type3FontRefs?.has(r))return errorFont();if(this.fontCache.has(r))return this.fontCache.get(r);try{t=this.xref.fetchIfRef(r)}catch(e){warn(`loadFont - lookup failed: "${e}".`)}}if(!(t instanceof Dict)){if(!this.options.ignoreErrors&&!this.parsingType3Font){warn(`Font "${e}" is not available.`);return errorFont()}warn(`Font "${e}" is not available -- attempting to fallback to a default font.`);t=a||PartialEvaluator.fallbackFontDict}if(t.cacheKey&&this.fontCache.has(t.cacheKey))return this.fontCache.get(t.cacheKey);const{promise:n,resolve:g}=Promise.withResolvers();let o;try{o=this.preEvaluateFont(t);o.cssFontInfo=s}catch(e){warn(`loadFont - preEvaluateFont failed: "${e}".`);return errorFont()}const{descriptor:c,hash:C}=o,h=r instanceof Ref;let l;if(C&&c instanceof Dict){const e=c.fontAliases||=Object.create(null);if(e[C]){const t=e[C].aliasRef;if(h&&t&&this.fontCache.has(t)){this.fontCache.putAlias(r,t);return this.fontCache.get(r)}}else e[C]={fontID:this.idFactory.createFontId()};h&&(e[C].aliasRef=r);l=e[C].fontID}else l=this.idFactory.createFontId();assert(l?.startsWith("f"),'The "fontID" must be (correctly) defined.');if(h)this.fontCache.put(r,n);else{t.cacheKey=`cacheKey_${l}`;this.fontCache.put(t.cacheKey,n)}t.loadedName=`${this.idFactory.getDocId()}_${l}`;this.translateFont(o).then((e=>{g(new TranslatedFont({loadedName:t.loadedName,font:e,dict:t,evaluatorOptions:this.options}))})).catch((e=>{warn(`loadFont - translateFont failed: "${e}".`);g(new TranslatedFont({loadedName:t.loadedName,font:new ErrorFont(e instanceof Error?e.message:e),dict:t,evaluatorOptions:this.options}))}));return n}buildPath(e,t,i,a=!1){const s=e.length-1;i||(i=[]);if(s<0||e.fnArray[s]!==it){if(a){warn(`Encountered path operator "${t}" inside of a text object.`);e.addOp(xA,null)}let s;switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];s=[Math.min(i[0],e),Math.min(i[1],t),Math.max(i[0],e),Math.max(i[1],t)];break;case LA:case HA:s=[i[0],i[1],i[0],i[1]];break;default:s=[1/0,1/0,-1/0,-1/0]}e.addOp(it,[[t],i,s]);a&&e.addOp(UA,null)}else{const a=e.argsArray[s];a[0].push(t);a[1].push(...i);const r=a[2];switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];r[0]=Math.min(r[0],i[0],e);r[1]=Math.min(r[1],i[1],t);r[2]=Math.max(r[2],i[0],e);r[3]=Math.max(r[3],i[1],t);break;case LA:case HA:r[0]=Math.min(r[0],i[0]);r[1]=Math.min(r[1],i[1]);r[2]=Math.max(r[2],i[0]);r[3]=Math.max(r[3],i[1])}}}parseColorSpace({cs:e,resources:t,localColorSpaceCache:i}){return ColorSpace.parseAsync({cs:e,xref:this.xref,resources:t,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:i}).catch((e=>{if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseColorSpace - ignoring ColorSpace: "${e}".`);return null}throw e}))}parseShading({shading:e,resources:t,localColorSpaceCache:i,localShadingPatternCache:a}){let s,r=a.get(e);if(r)return r;try{s=Pattern.parseShading(e,this.xref,t,this._pdfFunctionFactory,i).getIR()}catch(t){if(t instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseShading - ignoring shading: "${t}".`);a.set(e,null);return null}throw t}r=`pattern_${this.idFactory.createObjId()}`;this.parsingType3Font&&(r=`${this.idFactory.getDocId()}_type3_${r}`);a.set(e,r);this.parsingType3Font?this.handler.send("commonobj",[r,"Pattern",s]):this.handler.send("obj",[r,this.pageIndex,"Pattern",s]);return r}handleColorN(e,t,i,a,s,r,n,g,o,c){const C=i.pop();if(C instanceof Name){const h=s.getRaw(C.name),l=h instanceof Ref&&o.getByRef(h);if(l)try{const s=a.base?a.base.getRgb(i,0):null,r=getTilingPatternIR(l.operatorListIR,l.dict,s);e.addOp(t,r);return}catch{}const Q=this.xref.fetchIfRef(h);if(Q){const s=Q instanceof BaseStream?Q.dict:Q,C=s.get("PatternType");if(C===Ns){const g=a.base?a.base.getRgb(i,0):null;return this.handleTilingType(t,g,r,Q,s,e,n,o)}if(C===Gs){const i=s.get("Shading"),a=this.parseShading({shading:i,resources:r,localColorSpaceCache:g,localShadingPatternCache:c});if(a){const i=lookupMatrix(s.getArray("Matrix"),null);e.addOp(t,["Shading",a,i])}return}throw new FormatError(`Unknown PatternType: ${C}`)}}throw new FormatError(`Unknown PatternName: ${C}`)}_parseVisibilityExpression(e,t,i){if(++t>10){warn("Visibility expression is too deeply nested");return}const a=e.length,s=this.xref.fetchIfRef(e[0]);if(!(a<2)&&s instanceof Name){switch(s.name){case"And":case"Or":case"Not":i.push(s.name);break;default:warn(`Invalid operator ${s.name} in visibility expression`);return}for(let s=1;s<a;s++){const a=e[s],r=this.xref.fetchIfRef(a);if(Array.isArray(r)){const e=[];i.push(e);this._parseVisibilityExpression(r,t,e)}else a instanceof Ref&&i.push(a.toString())}}else warn("Invalid visibility expression")}async parseMarkedContentProps(e,t){let i;if(e instanceof Name){i=t.get("Properties").get(e.name)}else{if(!(e instanceof Dict))throw new FormatError("Optional content properties malformed.");i=e}const a=i.get("Type")?.name;if("OCG"===a)return{type:a,id:i.objId};if("OCMD"===a){const e=i.get("VE");if(Array.isArray(e)){const t=[];this._parseVisibilityExpression(e,0,t);if(t.length>0)return{type:"OCMD",expression:t}}const t=i.get("OCGs");if(Array.isArray(t)||t instanceof Dict){const e=[];if(Array.isArray(t))for(const i of t)e.push(i.toString());else e.push(t.objId);return{type:a,ids:e,policy:i.get("P")instanceof Name?i.get("P").name:null,expression:null}}if(t instanceof Ref)return{type:a,id:t.toString()}}return null}getOperatorList({stream:e,task:t,resources:i,operatorList:a,initialState:s=null,fallbackFontDict:r=null}){i||=Dict.empty;s||=new EvalState;if(!a)throw new Error('getOperatorList: missing "operatorList" parameter');const n=this,g=this.xref;let o=!1;const c=new LocalImageCache,C=new LocalColorSpaceCache,h=new LocalGStateCache,l=new LocalTilingPatternCache,Q=new Map,E=i.get("XObject")||Dict.empty,u=i.get("Pattern")||Dict.empty,d=new StateManager(s),f=new EvaluatorPreprocessor(e,g,d),p=new TimeSlotManager;function closePendingRestoreOPS(e){for(let e=0,t=f.savedStatesDepth;e<t;e++)a.addOp(UA,[])}return new Promise((function promiseBody(e,s){const next=function(t){Promise.all([t,a.ready]).then((function(){try{promiseBody(e,s)}catch(e){s(e)}}),s)};t.ensureNotTerminated();p.reset();const m={};let y,w,D,b,F,S;for(;!(y=p.check());){m.args=null;if(!f.read(m))break;let e=m.args,s=m.fn;switch(0|s){case Me:S=e[0]instanceof Name;F=e[0].name;if(S){const t=c.getByName(F);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("XObject must be referred to by name.");let r=E.getRaw(F);if(r instanceof Ref){const t=c.getByRef(r)||n._regionalImageCache.getByRef(r);if(t){addLocallyCachedImageOps(a,t);e();return}const i=n.globalImageCache.getData(r,n.pageIndex);if(i){a.addDependency(i.objId);a.addImageOps(i.fn,i.args,i.optionalContent);e();return}r=g.fetch(r)}if(!(r instanceof BaseStream))throw new FormatError("XObject should be a stream");const o=r.dict.get("Subtype");if(!(o instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==o.name)if("Image"!==o.name){if("PS"!==o.name)throw new FormatError(`Unhandled XObject subtype ${o.name}`);info("Ignored XObject subtype PS");e()}else n.buildPaintImageXObject({resources:i,image:r,operatorList:a,cacheKey:F,localImageCache:c,localColorSpaceCache:C}).then(e,s);else{d.save();n.buildFormXObject(i,r,null,a,t,d.state.clone(),C).then((function(){d.restore();e()}),s)}})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring XObject: "${e}".`)}})));return;case re:var k=e[1];next(n.handleSetFont(i,e,null,a,t,d.state,r).then((function(e){a.addDependency(e);a.addOp(re,[e,k])})));return;case Ae:o=!0;break;case ee:o=!1;break;case Ue:var R=e[0].cacheKey;if(R){const t=c.getByName(R);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(n.buildPaintImageXObject({resources:i,image:e[0],isInline:!0,operatorList:a,cacheKey:R,localImageCache:c,localColorSpaceCache:C}));return;case he:if(!d.state.font){n.ensureStateFont(d.state);continue}e[0]=n.handleText(e[0],d.state);break;case Be:if(!d.state.font){n.ensureStateFont(d.state);continue}var N=[],G=d.state;for(const t of e[0])"string"==typeof t?N.push(...n.handleText(t,G)):"number"==typeof t&&N.push(t);e[0]=N;s=he;break;case le:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);e[0]=n.handleText(e[0],d.state);s=he;break;case Qe:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);a.addOp(ie,[e.shift()]);a.addOp(te,[e.shift()]);e[0]=n.handleText(e[0],d.state);s=he;break;case ne:d.state.textRenderingMode=e[0];break;case fe:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.fillColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.fillColorSpace=e||ColorSpace.singletons.gray})));return}case de:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.strokeColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.strokeColorSpace=e||ColorSpace.singletons.gray})));return}case ye:b=d.state.fillColorSpace;e=b.getRgb(e,0);s=Se;break;case pe:b=d.state.strokeColorSpace;e=b.getRgb(e,0);s=Fe;break;case be:d.state.fillColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Se;break;case De:d.state.strokeColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Fe;break;case Re:d.state.fillColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Se;break;case ke:d.state.strokeColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Fe;break;case Se:d.state.fillColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case Fe:d.state.strokeColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case we:b=d.state.patternFillColorSpace;if(!b){e=[];s=st;break}if("Pattern"===b.name){next(n.handleColorN(a,we,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Se;break;case me:b=d.state.patternStrokeColorSpace;if(!b){e=[];s=at;break}if("Pattern"===b.name){next(n.handleColorN(a,me,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Fe;break;case Ne:let f;try{const t=i.get("Shading");if(!t)throw new FormatError("No shading resource found");f=t.get(e[0].name);if(!f)throw new FormatError("No shading object found")}catch(e){if(e instanceof AbortException)continue;if(n.options.ignoreErrors){warn(`getOperatorList - ignoring Shading: "${e}".`);continue}throw e}const p=n.parseShading({shading:f,resources:i,localColorSpaceCache:C,localShadingPatternCache:Q});if(!p)continue;e=[p];s=Ne;break;case GA:S=e[0]instanceof Name;F=e[0].name;if(S){const t=h.getByName(F);if(t){t.length>0&&a.addOp(GA,[t]);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("GState must be referred to by name.");const r=i.get("ExtGState");if(!(r instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const g=r.get(F);if(!(g instanceof Dict))throw new FormatError("GState should be a dictionary.");n.setGState({resources:i,gState:g,operatorList:a,cacheKey:F,task:t,stateManager:d,localGStateCache:h,localColorSpaceCache:C}).then(e,s)})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring ExtGState: "${e}".`)}})));return;case LA:case HA:case JA:case YA:case vA:case KA:case TA:n.buildPath(a,s,e,o);continue;case Le:case He:case Ke:case Te:continue;case Ye:if(!(e[0]instanceof Name)){warn(`Expected name for beginMarkedContentProps arg0=${e[0]}`);a.addOp(Ye,["OC",null]);continue}if("OC"===e[0].name){next(n.parseMarkedContentProps(e[1],i).then((e=>{a.addOp(Ye,["OC",e])})).catch((e=>{if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring beginMarkedContentProps: "${e}".`);a.addOp(Ye,["OC",null])}})));return}e=[e[0].name,e[1]instanceof Dict?e[1].get("MCID"):null];break;default:if(null!==e){for(w=0,D=e.length;w<D&&!(e[w]instanceof Dict);w++);if(w<D){warn("getOperatorList - ignoring operator: "+s);continue}}}a.addOp(s,e)}if(y)next(xs);else{closePendingRestoreOPS();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring errors during "${t.name}" task: "${e}".`);closePendingRestoreOPS()}}))}getTextContent({stream:e,task:t,resources:s,stateManager:r=null,includeMarkedContent:n=!1,sink:g,seenStyles:o=new Set,viewBox:c,lang:C=null,markedContentData:h=null,disableNormalization:l=!1,keepWhiteSpace:Q=!1}){s||=Dict.empty;r||=new StateManager(new TextState);n&&(h||={level:0});const E={items:[],styles:Object.create(null),lang:C},u={initialized:!1,str:[],totalWidth:0,totalHeight:0,width:0,height:0,vertical:!1,prevTransform:null,textAdvanceScale:0,spaceInFlowMin:0,spaceInFlowMax:0,trackingSpaceMin:1/0,negativeSpaceMax:-1/0,notASpace:-1/0,transform:null,fontName:null,hasEOL:!1},d=[" "," "];let f=0;function saveLastChar(e){const t=(f+1)%2,i=" "!==d[f]&&" "===d[t];d[f]=e;f=t;return!Q&&i}function shouldAddWhitepsace(){return!Q&&" "!==d[f]&&" "===d[(f+1)%2]}function resetLastChars(){d[0]=d[1]=" ";f=0}const p=this,m=this.xref,y=[];let w=null;const D=new LocalImageCache,b=new LocalGStateCache,F=new EvaluatorPreprocessor(e,m,r);let S;function pushWhitespace({width:e=0,height:t=0,transform:i=u.prevTransform,fontName:a=u.fontName}){E.items.push({str:" ",dir:"ltr",width:e,height:t,transform:i,fontName:a,hasEOL:!1})}function getCurrentTextTransform(){const e=S.font,t=[S.fontSize*S.textHScale,0,0,S.fontSize,0,S.textRise];if(e.isType3Font&&(S.fontSize<=1||e.isCharBBox)&&!isArrayEqual(S.fontMatrix,a)){const i=e.bbox[3]-e.bbox[1];i>0&&(t[3]*=i*S.fontMatrix[3])}return Util.transform(S.ctm,Util.transform(S.textMatrix,t))}function ensureTextContentItem(){if(u.initialized)return u;const{font:e,loadedName:t}=S;if(!o.has(t)){o.add(t);E.styles[t]={fontFamily:e.fallbackName,ascent:e.ascent,descent:e.descent,vertical:e.vertical};if(p.options.fontExtraProperties&&e.systemFontInfo){const i=E.styles[t];i.fontSubstitution=e.systemFontInfo.css;i.fontSubstitutionLoadedName=e.systemFontInfo.loadedName}}u.fontName=t;const i=u.transform=getCurrentTextTransform();if(e.vertical){u.width=u.totalWidth=Math.hypot(i[0],i[1]);u.height=u.totalHeight=0;u.vertical=!0}else{u.width=u.totalWidth=0;u.height=u.totalHeight=Math.hypot(i[2],i[3]);u.vertical=!1}const a=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),s=Math.hypot(S.ctm[0],S.ctm[1]);u.textAdvanceScale=s*a;const{fontSize:r}=S;u.trackingSpaceMin=.102*r;u.notASpace=.03*r;u.negativeSpaceMax=-.2*r;u.spaceInFlowMin=.102*r;u.spaceInFlowMax=.6*r;u.hasEOL=!1;u.initialized=!0;return u}function updateAdvanceScale(){if(!u.initialized)return;const e=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),t=Math.hypot(S.ctm[0],S.ctm[1])*e;if(t!==u.textAdvanceScale){if(u.vertical){u.totalHeight+=u.height*u.textAdvanceScale;u.height=0}else{u.totalWidth+=u.width*u.textAdvanceScale;u.width=0}u.textAdvanceScale=t}}function runBidiTransform(e){let t=e.str.join("");l||(t=function normalizeUnicode(e){if(!Ct){Ct=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;ht=new Map([["ſt","ſt"]])}return e.replaceAll(Ct,((e,t,i)=>t?t.normalize("NFKC"):ht.get(i)))}(t));const i=bidi(t,-1,e.vertical);return{str:i.str,dir:i.dir,width:Math.abs(e.totalWidth),height:Math.abs(e.totalHeight),transform:e.transform,fontName:e.fontName,hasEOL:e.hasEOL}}async function handleSetFont(e,i){const r=await p.loadFont(e,i,s);if(r.font.isType3Font)try{await r.loadType3Data(p,s,t)}catch{}S.loadedName=r.loadedName;S.font=r.font;S.fontMatrix=r.font.fontMatrix||a}function applyInverseRotation(e,t,i){const a=Math.hypot(i[0],i[1]);return[(i[0]*e+i[1]*t)/a,(i[2]*e+i[3]*t)/a]}function compareWithLastPosition(e){const t=getCurrentTextTransform();let i=t[4],a=t[5];if(S.font?.vertical){if(i<c[0]||i>c[2]||a+e<c[1]||a>c[3])return!1}else if(i+e<c[0]||i>c[2]||a<c[1]||a>c[3])return!1;if(!S.font||!u.prevTransform)return!0;let s=u.prevTransform[4],r=u.prevTransform[5];if(s===i&&r===a)return!0;let n=-1;t[0]&&0===t[1]&&0===t[2]?n=t[0]>0?0:180:t[1]&&0===t[0]&&0===t[3]&&(n=t[1]>0?90:270);switch(n){case 0:break;case 90:[i,a]=[a,i];[s,r]=[r,s];break;case 180:[i,a,s,r]=[-i,-a,-s,-r];break;case 270:[i,a]=[-a,-i];[s,r]=[-r,-s];break;default:[i,a]=applyInverseRotation(i,a,t);[s,r]=applyInverseRotation(s,r,u.prevTransform)}if(S.font.vertical){const e=(r-a)/u.textAdvanceScale,t=i-s,n=Math.sign(u.height);if(e<n*u.negativeSpaceMax){if(Math.abs(t)>.5*u.width){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(t)>u.width){appendEOL();return!0}e<=n*u.notASpace&&resetLastChars();if(e<=n*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else u.height+=e;else if(!addFakeSpaces(e,u.prevTransform,n))if(0===u.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else u.height+=e;Math.abs(t)>.25*u.width&&flushTextContentItem();return!0}const g=(i-s)/u.textAdvanceScale,o=a-r,C=Math.sign(u.width);if(g<C*u.negativeSpaceMax){if(Math.abs(o)>.5*u.height){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(o)>u.height){appendEOL();return!0}g<=C*u.notASpace&&resetLastChars();if(g<=C*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({width:Math.abs(g)})}else u.width+=g;else if(!addFakeSpaces(g,u.prevTransform,C))if(0===u.str.length){resetLastChars();pushWhitespace({width:Math.abs(g)})}else u.width+=g;Math.abs(o)>.25*u.height&&flushTextContentItem();return!0}function buildTextContentItem({chars:e,extraSpacing:t}){const i=S.font;if(!e){const e=S.charSpacing+t;e&&(i.vertical?S.translateTextMatrix(0,-e):S.translateTextMatrix(e*S.textHScale,0));Q&&compareWithLastPosition(0);return}const a=i.charsToGlyphs(e),s=S.fontMatrix[0]*S.fontSize;for(let e=0,r=a.length;e<r;e++){const n=a[e],{category:g}=n;if(g.isInvisibleFormatMark)continue;let o=S.charSpacing+(e+1===r?t:0),c=n.width;i.vertical&&(c=n.vmetric?n.vmetric[0]:-c);let C=c*s;if(!Q&&g.isWhitespace){if(i.vertical){o+=-C+S.wordSpacing;S.translateTextMatrix(0,-o)}else{o+=C+S.wordSpacing;S.translateTextMatrix(o*S.textHScale,0)}saveLastChar(" ");continue}if(!g.isZeroWidthDiacritic&&!compareWithLastPosition(C)){i.vertical?S.translateTextMatrix(0,C):S.translateTextMatrix(C*S.textHScale,0);continue}const h=ensureTextContentItem();g.isZeroWidthDiacritic&&(C=0);if(i.vertical){S.translateTextMatrix(0,C);C=Math.abs(C);h.height+=C}else{C*=S.textHScale;S.translateTextMatrix(C,0);h.width+=C}C&&(h.prevTransform=getCurrentTextTransform());const l=n.unicode;saveLastChar(l)&&h.str.push(" ");h.str.push(l);o&&(i.vertical?S.translateTextMatrix(0,-o):S.translateTextMatrix(o*S.textHScale,0))}}function appendEOL(){resetLastChars();if(u.initialized){u.hasEOL=!0;flushTextContentItem()}else E.items.push({str:"",dir:"ltr",width:0,height:0,transform:getCurrentTextTransform(),fontName:S.loadedName,hasEOL:!0})}function addFakeSpaces(e,t,i){if(i*u.spaceInFlowMin<=e&&e<=i*u.spaceInFlowMax){if(u.initialized){resetLastChars();u.str.push(" ")}return!1}const a=u.fontName;let s=0;if(u.vertical){s=e;e=0}flushTextContentItem();resetLastChars();pushWhitespace({width:Math.abs(e),height:Math.abs(s),transform:t||getCurrentTextTransform(),fontName:a});return!0}function flushTextContentItem(){if(u.initialized&&u.str){u.vertical?u.totalHeight+=u.height*u.textAdvanceScale:u.totalWidth+=u.width*u.textAdvanceScale;E.items.push(runBidiTransform(u));u.initialized=!1;u.str.length=0}}function enqueueChunk(e=!1){const t=E.items.length;if(0!==t&&!(e&&t<10)){g.enqueue(E,t);E.items=[];E.styles=Object.create(null)}}const k=new TimeSlotManager;return new Promise((function promiseBody(e,a){const next=function(t){enqueueChunk(!0);Promise.all([t,g.ready]).then((function(){try{promiseBody(e,a)}catch(e){a(e)}}),a)};t.ensureNotTerminated();k.reset();const u={};let d,f=[];for(;!(d=k.check());){f.length=0;u.args=f;if(!F.read(u))break;const e=S;S=r.state;const a=u.fn;f=u.args;switch(0|a){case re:var R=f[0].name,N=f[1];if(S.font&&R===S.fontName&&N===S.fontSize)break;flushTextContentItem();S.fontName=R;S.fontSize=N;next(handleSetFont(R,null));return;case ge:S.textRise=f[0];break;case ae:S.textHScale=f[0]/100;break;case se:S.leading=f[0];break;case oe:S.translateTextLineMatrix(f[0],f[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ie:S.leading=-f[1];S.translateTextLineMatrix(f[0],f[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ce:S.carriageReturn();break;case ce:S.setTextMatrix(f[0],f[1],f[2],f[3],f[4],f[5]);S.setTextLineMatrix(f[0],f[1],f[2],f[3],f[4],f[5]);updateAdvanceScale();break;case te:S.charSpacing=f[0];break;case ie:S.wordSpacing=f[0];break;case Ae:S.textMatrix=i.slice();S.textLineMatrix=i.slice();break;case Be:if(!r.state.font){p.ensureStateFont(r.state);continue}const a=(S.font.vertical?1:-1)*S.fontSize/1e3,u=f[0];for(let e=0,t=u.length;e<t;e++){const t=u[e];if("string"==typeof t)y.push(t);else if("number"==typeof t&&0!==t){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:t*a})}}if(y.length>0){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:0})}break;case he:if(!r.state.font){p.ensureStateFont(r.state);continue}buildTextContentItem({chars:f[0],extraSpacing:0});break;case le:if(!r.state.font){p.ensureStateFont(r.state);continue}S.carriageReturn();buildTextContentItem({chars:f[0],extraSpacing:0});break;case Qe:if(!r.state.font){p.ensureStateFont(r.state);continue}S.wordSpacing=f[0];S.charSpacing=f[1];S.carriageReturn();buildTextContentItem({chars:f[2],extraSpacing:0});break;case Me:flushTextContentItem();w||(w=s.get("XObject")||Dict.empty);var G=f[0]instanceof Name,x=f[0].name;if(G&&D.getByName(x))break;next(new Promise((function(e,i){if(!G)throw new FormatError("XObject must be referred to by name.");let a=w.getRaw(x);if(a instanceof Ref){if(D.getByRef(a)){e();return}if(p.globalImageCache.getData(a,p.pageIndex)){e();return}a=m.fetch(a)}if(!(a instanceof BaseStream))throw new FormatError("XObject should be a stream");const E=a.dict.get("Subtype");if(!(E instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==E.name){D.set(x,a.dict.objId,!0);e();return}const u=r.state.clone(),d=new StateManager(u),f=lookupMatrix(a.dict.getArray("Matrix"),null);f&&d.transform(f);enqueueChunk();const y={enqueueInvoked:!1,enqueue(e,t){this.enqueueInvoked=!0;g.enqueue(e,t)},get desiredSize(){return g.desiredSize},get ready(){return g.ready}};p.getTextContent({stream:a,task:t,resources:a.dict.get("Resources")||s,stateManager:d,includeMarkedContent:n,sink:y,seenStyles:o,viewBox:c,lang:C,markedContentData:h,disableNormalization:l,keepWhiteSpace:Q}).then((function(){y.enqueueInvoked||D.set(x,a.dict.objId,!0);e()}),i)})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring XObject: "${e}".`)}})));return;case GA:G=f[0]instanceof Name;x=f[0].name;if(G&&b.getByName(x))break;next(new Promise((function(e,t){if(!G)throw new FormatError("GState must be referred to by name.");const i=s.get("ExtGState");if(!(i instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const a=i.get(x);if(!(a instanceof Dict))throw new FormatError("GState should be a dictionary.");const r=a.get("Font");if(r){flushTextContentItem();S.fontName=null;S.fontSize=r[1];handleSetFont(null,r[0]).then(e,t)}else{b.set(x,a.objId,!0);e()}})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring ExtGState: "${e}".`)}})));return;case Je:flushTextContentItem();if(n){h.level++;E.items.push({type:"beginMarkedContent",tag:f[0]instanceof Name?f[0].name:null})}break;case Ye:flushTextContentItem();if(n){h.level++;let e=null;f[1]instanceof Dict&&(e=f[1].get("MCID"));E.items.push({type:"beginMarkedContentProps",id:Number.isInteger(e)?`${p.idFactory.getPageObjId()}_mc${e}`:null,tag:f[0]instanceof Name?f[0].name:null})}break;case ve:flushTextContentItem();if(n){if(0===h.level)break;h.level--;E.items.push({type:"endMarkedContent"})}break;case UA:!e||e.font===S.font&&e.fontSize===S.fontSize&&e.fontName===S.fontName||flushTextContentItem()}if(E.items.length>=g.desiredSize){d=!0;break}}if(d)next(xs);else{flushTextContentItem();enqueueChunk();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getTextContent - ignoring errors during "${t.name}" task: "${e}".`);flushTextContentItem();enqueueChunk()}}))}async extractDataStructures(e,t){const i=this.xref;let a;const s=this.readToUnicode(t.toUnicode);if(t.composite){const i=e.get("CIDSystemInfo");i instanceof Dict&&(t.cidSystemInfo={registry:stringToPDFString(i.get("Registry")),ordering:stringToPDFString(i.get("Ordering")),supplement:i.get("Supplement")});try{const t=e.get("CIDToGIDMap");t instanceof BaseStream&&(a=t.getBytes())}catch(e){if(!this.options.ignoreErrors)throw e;warn(`extractDataStructures - ignoring CIDToGIDMap data: "${e}".`)}}const r=[];let n,g=null;if(e.has("Encoding")){n=e.get("Encoding");if(n instanceof Dict){g=n.get("BaseEncoding");g=g instanceof Name?g.name:null;if(n.has("Differences")){const e=n.get("Differences");let t=0;for(const a of e){const e=i.fetchIfRef(a);if("number"==typeof e)t=e;else{if(!(e instanceof Name))throw new FormatError(`Invalid entry in 'Differences' array: ${e}`);r[t++]=e.name}}}}else if(n instanceof Name)g=n.name;else{const e="Encoding is not a Name nor a Dict";if(!this.options.ignoreErrors)throw new FormatError(e);warn(e)}"MacRomanEncoding"!==g&&"MacExpertEncoding"!==g&&"WinAnsiEncoding"!==g&&(g=null)}const o=!t.file||t.isInternalFont,c=$i()[t.name];g&&o&&c&&(g=null);if(g)t.defaultEncoding=getEncoding(g);else{const e=!!(t.flags&Pi),i=!!(t.flags&Wi);n=yi;"TrueType"!==t.type||i||(n=wi);if(e||c){n=mi;o&&(/Symbol/i.test(t.name)?n=Di:/Dingbats/i.test(t.name)?n=bi:/Wingdings/i.test(t.name)&&(n=wi))}t.defaultEncoding=n}t.differences=r;t.baseEncodingName=g;t.hasEncoding=!!g||r.length>0;t.dict=e;t.toUnicode=await s;const C=await this.buildToUnicode(t);t.toUnicode=C;a&&(t.cidToGidMap=this.readCidToGidMap(a,C));return t}_simpleFontToUnicode(e,t=!1){assert(!e.composite,"Must be a simple font.");const i=[],a=e.defaultEncoding.slice(),s=e.baseEncodingName,r=e.differences;for(const e in r){const t=r[e];".notdef"!==t&&(a[e]=t)}const n=Mi();for(const r in a){let g=a[r];if(""===g)continue;let o=n[g];if(void 0!==o){i[r]=String.fromCharCode(o);continue}let c=0;switch(g[0]){case"G":3===g.length&&(c=parseInt(g.substring(1),16));break;case"g":5===g.length&&(c=parseInt(g.substring(1),16));break;case"C":case"c":if(g.length>=3&&g.length<=4){const i=g.substring(1);if(t){c=parseInt(i,16);break}c=+i;if(Number.isNaN(c)&&Number.isInteger(parseInt(i,16)))return this._simpleFontToUnicode(e,!0)}break;case"u":o=getUnicodeForGlyph(g,n);-1!==o&&(c=o);break;default:switch(g){case"f_h":case"f_t":case"T_h":i[r]=g.replaceAll("_","");continue}}if(c>0&&c<=1114111&&Number.isInteger(c)){if(s&&c===+r){const e=getEncoding(s);if(e&&(g=e[r])){i[r]=String.fromCharCode(n[g]);continue}}i[r]=String.fromCodePoint(c)}}return i}async buildToUnicode(e){e.hasIncludedToUnicodeMap=e.toUnicode?.length>0;if(e.hasIncludedToUnicodeMap){!e.composite&&e.hasEncoding&&(e.fallbackToUnicode=this._simpleFontToUnicode(e));return e.toUnicode}if(!e.composite)return new ToUnicodeMap(this._simpleFontToUnicode(e));if(e.composite&&(e.cMap.builtInCMap&&!(e.cMap instanceof IdentityCMap)||"Adobe"===e.cidSystemInfo?.registry&&("GB1"===e.cidSystemInfo.ordering||"CNS1"===e.cidSystemInfo.ordering||"Japan1"===e.cidSystemInfo.ordering||"Korea1"===e.cidSystemInfo.ordering))){const{registry:t,ordering:i}=e.cidSystemInfo,a=Name.get(`${t}-${i}-UCS2`),s=await CMapFactory.create({encoding:a,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null}),r=[],n=[];e.cMap.forEach((function(e,t){if(t>65535)throw new FormatError("Max size of CID is 65,535");const i=s.lookup(t);if(i){n.length=0;for(let e=0,t=i.length;e<t;e+=2)n.push((i.charCodeAt(e)<<8)+i.charCodeAt(e+1));r[e]=String.fromCharCode(...n)}}));return new ToUnicodeMap(r)}return new IdentityToUnicodeMap(e.firstChar,e.lastChar)}async readToUnicode(e){if(!e)return null;if(e instanceof Name){const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});return t instanceof IdentityCMap?new IdentityToUnicodeMap(0,65535):new ToUnicodeMap(t.getMap())}if(e instanceof BaseStream)try{const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});if(t instanceof IdentityCMap)return new IdentityToUnicodeMap(0,65535);const i=new Array(t.length);t.forEach((function(e,t){if("number"==typeof t){i[e]=String.fromCodePoint(t);return}t.length%2!=0&&(t="\0"+t);const a=[];for(let e=0;e<t.length;e+=2){const i=t.charCodeAt(e)<<8|t.charCodeAt(e+1);if(55296!=(63488&i)){a.push(i);continue}e+=2;const s=t.charCodeAt(e)<<8|t.charCodeAt(e+1);a.push(((1023&i)<<10)+(1023&s)+65536)}i[e]=String.fromCodePoint(...a)}));return new ToUnicodeMap(i)}catch(e){if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`readToUnicode - ignoring ToUnicode data: "${e}".`);return null}throw e}return null}readCidToGidMap(e,t){const i=[];for(let a=0,s=e.length;a<s;a++){const s=e[a++]<<8|e[a],r=a>>1;(0!==s||t.has(r))&&(i[r]=s)}return i}extractWidths(e,t,i){const a=this.xref;let s=[],r=0;const n=[];let g;if(i.composite){const t=e.get("DW");r="number"==typeof t?Math.ceil(t):1e3;const o=e.get("W");if(Array.isArray(o))for(let e=0,t=o.length;e<t;e++){let t=a.fetchIfRef(o[e++]);if(!Number.isInteger(t))break;const i=a.fetchIfRef(o[e]);if(Array.isArray(i))for(const e of i){const i=a.fetchIfRef(e);"number"==typeof i&&(s[t]=i);t++}else{if(!Number.isInteger(i))break;{const r=a.fetchIfRef(o[++e]);if("number"!=typeof r)continue;for(let e=t;e<=i;e++)s[e]=r}}}if(i.vertical){const t=e.getArray("DW2");let i=isNumberArray(t,2)?t:[880,-1e3];g=[i[1],.5*r,i[0]];i=e.get("W2");if(Array.isArray(i))for(let e=0,t=i.length;e<t;e++){let t=a.fetchIfRef(i[e++]);if(!Number.isInteger(t))break;const s=a.fetchIfRef(i[e]);if(Array.isArray(s))for(let e=0,i=s.length;e<i;e++){const i=[a.fetchIfRef(s[e++]),a.fetchIfRef(s[e++]),a.fetchIfRef(s[e])];isNumberArray(i,null)&&(n[t]=i);t++}else{if(!Number.isInteger(s))break;{const r=[a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e])];if(!isNumberArray(r,null))continue;for(let e=t;e<=s;e++)n[e]=r}}}}}else{const n=e.get("Widths");if(Array.isArray(n)){let e=i.firstChar;for(const t of n){const i=a.fetchIfRef(t);"number"==typeof i&&(s[e]=i);e++}const g=t.get("MissingWidth");r="number"==typeof g?g:0}else{const t=e.get("BaseFont");if(t instanceof Name){const e=this.getBaseFontMetrics(t.name);s=this.buildCharCodeToWidth(e.widths,i);r=e.defaultWidth}}}let o=!0,c=r;for(const e in s){const t=s[e];if(t)if(c){if(c!==t){o=!1;break}}else c=t}o?i.flags|=qi:i.flags&=~qi;i.defaultWidth=r;i.widths=s;i.defaultVMetrics=g;i.vmetrics=n}isSerifFont(e){const t=e.split("-",1)[0];return t in _i()||/serif/gi.test(t)}getBaseFontMetrics(e){let t=0,i=Object.create(null),a=!1;let s=Zi()[e]||e;const r=aa();s in r||(s=this.isSerifFont(e)?"Times-Roman":"Helvetica");const n=r[s];if("number"==typeof n){t=n;a=!0}else i=n();return{defaultWidth:t,monospace:a,widths:i}}buildCharCodeToWidth(e,t){const i=Object.create(null),a=t.differences,s=t.defaultEncoding;for(let t=0;t<256;t++)t in a&&e[a[t]]?i[t]=e[a[t]]:t in s&&e[s[t]]&&(i[t]=e[s[t]]);return i}preEvaluateFont(e){const t=e;let i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");let a,s=!1;if("Type0"===i.name){const t=e.get("DescendantFonts");if(!t)throw new FormatError("Descendant fonts are not specified");if(!((e=Array.isArray(t)?this.xref.fetchIfRef(t[0]):t)instanceof Dict))throw new FormatError("Descendant font is not a dictionary.");i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");s=!0}let r=e.get("FirstChar");Number.isInteger(r)||(r=0);let n=e.get("LastChar");Number.isInteger(n)||(n=s?65535:255);const g=e.get("FontDescriptor"),o=e.get("ToUnicode")||t.get("ToUnicode");if(g){a=new MurmurHash3_64;const i=t.getRaw("Encoding");if(i instanceof Name)a.update(i.name);else if(i instanceof Ref)a.update(i.toString());else if(i instanceof Dict)for(const e of i.getRawValues())if(e instanceof Name)a.update(e.name);else if(e instanceof Ref)a.update(e.toString());else if(Array.isArray(e)){const t=e.length,i=new Array(t);for(let a=0;a<t;a++){const t=e[a];t instanceof Name?i[a]=t.name:("number"==typeof t||t instanceof Ref)&&(i[a]=t.toString())}a.update(i.join())}a.update(`${r}-${n}`);if(o instanceof BaseStream){const e=o.str||o,t=e.buffer?new Uint8Array(e.buffer.buffer,0,e.bufferLength):new Uint8Array(e.bytes.buffer,e.start,e.end-e.start);a.update(t)}else o instanceof Name&&a.update(o.name);const g=e.get("Widths")||t.get("Widths");if(Array.isArray(g)){const e=[];for(const t of g)("number"==typeof t||t instanceof Ref)&&e.push(t.toString());a.update(e.join())}if(s){a.update("compositeFont");const i=e.get("W")||t.get("W");if(Array.isArray(i)){const e=[];for(const t of i)if("number"==typeof t||t instanceof Ref)e.push(t.toString());else if(Array.isArray(t)){const i=[];for(const e of t)("number"==typeof e||e instanceof Ref)&&i.push(e.toString());e.push(`[${i.join()}]`)}a.update(e.join())}const s=e.getRaw("CIDToGIDMap")||t.getRaw("CIDToGIDMap");s instanceof Name?a.update(s.name):s instanceof Ref?a.update(s.toString()):s instanceof BaseStream&&a.update(s.peekBytes())}}return{descriptor:g,dict:e,baseDict:t,composite:s,type:i.name,firstChar:r,lastChar:n,toUnicode:o,hash:a?a.hexdigest():""}}async translateFont({descriptor:e,dict:t,baseDict:i,composite:s,type:r,firstChar:n,lastChar:g,toUnicode:o,cssFontInfo:c}){const C="Type3"===r;if(!e){if(!C){let e=t.get("BaseFont");if(!(e instanceof Name))throw new FormatError("Base font is not specified");e=e.name.replaceAll(/[,_]/g,"-");const a=this.getBaseFontMetrics(e),s=e.split("-",1)[0],c=(this.isSerifFont(s)?Oi:0)|(a.monospace?qi:0)|($i()[s]?Pi:Wi),h={type:r,name:e,loadedName:i.loadedName,systemFontInfo:null,widths:a.widths,defaultWidth:a.defaultWidth,isSimulatedFlags:!0,flags:c,firstChar:n,lastChar:g,toUnicode:o,xHeight:0,capHeight:0,italicAngle:0,isType3Font:C},l=t.get("Widths"),Q=getStandardFontName(e);let E=null;if(Q){E=await this.fetchStandardFontData(Q);h.isInternalFont=!!E}!h.isInternalFont&&this.options.useSystemFonts&&(h.systemFontInfo=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,e,Q,r));const u=await this.extractDataStructures(t,h);if(Array.isArray(l)){const e=[];let t=n;for(const i of l){const a=this.xref.fetchIfRef(i);"number"==typeof a&&(e[t]=a);t++}u.widths=e}else u.widths=this.buildCharCodeToWidth(a.widths,u);return new Font(e,E,u)}{const i=lookupNormalRect(t.getArray("FontBBox"),[0,0,0,0]);(e=new Dict(null)).set("FontName",Name.get(r));e.set("FontBBox",i)}}let h=e.get("FontName"),l=t.get("BaseFont");"string"==typeof h&&(h=Name.get(h));"string"==typeof l&&(l=Name.get(l));const Q=h?.name,E=l?.name;if(!C&&Q!==E){info(`The FontDescriptor's FontName is "${Q}" but should be the same as the Font's BaseFont "${E}".`);Q&&E&&(E.startsWith(Q)||!isKnownFontName(Q)&&isKnownFontName(E))&&(h=null)}h||=l;if(!(h instanceof Name))throw new FormatError("invalid font name");let u,d,f,p,m;try{u=e.get("FontFile","FontFile2","FontFile3")}catch(e){if(!this.options.ignoreErrors)throw e;warn(`translateFont - fetching "${h.name}" font file: "${e}".`);u=new NullStream}let y=!1,w=null,D=null;if(u){if(u.dict){const e=u.dict.get("Subtype");e instanceof Name&&(d=e.name);f=u.dict.get("Length1");p=u.dict.get("Length2");m=u.dict.get("Length3")}}else if(c){const e=getXfaFontName(h.name);if(e){c.fontFamily=`${c.fontFamily}-PdfJS-XFA`;c.metrics=e.metrics||null;w=e.factors||null;u=await this.fetchStandardFontData(e.name);y=!!u;i=t=getXfaFontDict(h.name);s=!0}}else if(!C){const e=getStandardFontName(h.name);if(e){u=await this.fetchStandardFontData(e);y=!!u}!y&&this.options.useSystemFonts&&(D=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,h.name,e,r))}const b=lookupMatrix(t.getArray("FontMatrix"),a),F=lookupNormalRect(e.getArray("FontBBox")||t.getArray("FontBBox"),void 0);let S=e.get("Ascent");"number"!=typeof S&&(S=void 0);let k=e.get("Descent");"number"!=typeof k&&(k=void 0);let R=e.get("XHeight");"number"!=typeof R&&(R=0);let N=e.get("CapHeight");"number"!=typeof N&&(N=0);let G=e.get("Flags");Number.isInteger(G)||(G=0);let x=e.get("ItalicAngle");"number"!=typeof x&&(x=0);const U={type:r,name:h.name,subtype:d,file:u,length1:f,length2:p,length3:m,isInternalFont:y,loadedName:i.loadedName,composite:s,fixedPitch:!1,fontMatrix:b,firstChar:n,lastChar:g,toUnicode:o,bbox:F,ascent:S,descent:k,xHeight:R,capHeight:N,flags:G,italicAngle:x,isType3Font:C,cssFontInfo:c,scaleFactors:w,systemFontInfo:D};if(s){const e=i.get("Encoding");e instanceof Name&&(U.cidEncoding=e.name);const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});U.cMap=t;U.vertical=U.cMap.vertical}const M=await this.extractDataStructures(t,U);this.extractWidths(t,e,M);return new Font(h.name,u,M)}static buildFontPaths(e,t,i,a){function buildPath(t){const s=`${e.loadedName}_path_${t}`;try{if(e.renderer.hasBuiltPath(t))return;i.send("commonobj",[s,"FontPath",e.renderer.getPathJs(t)])}catch(e){if(a.ignoreErrors){warn(`buildFontPaths - ignoring ${s} glyph: "${e}".`);return}throw e}}for(const e of t){buildPath(e.fontChar);const t=e.accent;t?.fontChar&&buildPath(t.fontChar)}}static get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("WinAnsiEncoding"));return shadow(this,"fallbackFontDict",e)}}class TranslatedFont{constructor({loadedName:e,font:t,dict:i,evaluatorOptions:a}){this.loadedName=e;this.font=t;this.dict=i;this._evaluatorOptions=a||Rs;this.type3Loaded=null;this.type3Dependencies=t.isType3Font?new Set:null;this.sent=!1}send(e){if(!this.sent){this.sent=!0;e.send("commonobj",[this.loadedName,"Font",this.font.exportData(this._evaluatorOptions.fontExtraProperties)])}}fallback(e){if(this.font.data){this.font.disableFontFace=!0;PartialEvaluator.buildFontPaths(this.font,this.font.glyphCacheValues,e,this._evaluatorOptions)}}loadType3Data(e,t,i){if(this.type3Loaded)return this.type3Loaded;if(!this.font.isType3Font)throw new Error("Must be a Type3 font.");const a=e.clone({ignoreErrors:!1}),s=new RefSet(e.type3FontRefs);this.dict.objId&&!s.has(this.dict.objId)&&s.put(this.dict.objId);a.type3FontRefs=s;const r=this.font,n=this.type3Dependencies;let g=Promise.resolve();const o=this.dict.get("CharProcs"),c=this.dict.get("Resources")||t,C=Object.create(null),h=Util.normalizeRect(r.bbox||[0,0,0,0]),l=h[2]-h[0],Q=h[3]-h[1],E=Math.hypot(l,Q);for(const e of o.getKeys())g=g.then((()=>{const t=o.get(e),s=new OperatorList;return a.getOperatorList({stream:t,task:i,resources:c,operatorList:s}).then((()=>{s.fnArray[0]===ue&&this._removeType3ColorOperators(s,E);C[e]=s.getIR();for(const e of s.dependencies)n.add(e)})).catch((function(t){warn(`Type3 font resource "${e}" is not available.`);const i=new OperatorList;C[e]=i.getIR()}))}));this.type3Loaded=g.then((()=>{r.charProcOperatorList=C;if(this._bbox){r.isCharBBox=!0;r.bbox=this._bbox}}));return this.type3Loaded}_removeType3ColorOperators(e,t=NaN){const i=Util.normalizeRect(e.argsArray[0].slice(2)),a=i[2]-i[0],s=i[3]-i[1],r=Math.hypot(a,s);if(0===a||0===s){e.fnArray.splice(0,1);e.argsArray.splice(0,1)}else if(0===t||Math.round(r/t)>=10){this._bbox||(this._bbox=[1/0,1/0,-1/0,-1/0]);this._bbox[0]=Math.min(this._bbox[0],i[0]);this._bbox[1]=Math.min(this._bbox[1],i[1]);this._bbox[2]=Math.max(this._bbox[2],i[2]);this._bbox[3]=Math.max(this._bbox[3],i[3])}let n=0,g=e.length;for(;n<g;){switch(e.fnArray[n]){case ue:break;case de:case fe:case pe:case me:case ye:case we:case De:case be:case Fe:case Se:case ke:case Re:case Ne:case RA:e.fnArray.splice(n,1);e.argsArray.splice(n,1);g--;continue;case GA:const[t]=e.argsArray[n];let i=0,a=t.length;for(;i<a;){const[e]=t[i];switch(e){case"TR":case"TR2":case"HT":case"BG":case"BG2":case"UCR":case"UCR2":t.splice(i,1);a--;continue}i++}}n++}}}class StateManager{constructor(e=new EvalState){this.state=e;this.stateStack=[]}save(){const e=this.state;this.stateStack.push(this.state);this.state=e.clone()}restore(){const e=this.stateStack.pop();e&&(this.state=e)}transform(e){this.state.ctm=Util.transform(this.state.ctm,e)}}class TextState{constructor(){this.ctm=new Float32Array(i);this.fontName=null;this.fontSize=0;this.loadedName=null;this.font=null;this.fontMatrix=a;this.textMatrix=i.slice();this.textLineMatrix=i.slice();this.charSpacing=0;this.wordSpacing=0;this.leading=0;this.textHScale=1;this.textRise=0}setTextMatrix(e,t,i,a,s,r){const n=this.textMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}setTextLineMatrix(e,t,i,a,s,r){const n=this.textLineMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}translateTextMatrix(e,t){const i=this.textMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}translateTextLineMatrix(e,t){const i=this.textLineMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}carriageReturn(){this.translateTextLineMatrix(0,-this.leading);this.textMatrix=this.textLineMatrix.slice()}clone(){const e=Object.create(this);e.textMatrix=this.textMatrix.slice();e.textLineMatrix=this.textLineMatrix.slice();e.fontMatrix=this.fontMatrix.slice();return e}}class EvalState{constructor(){this.ctm=new Float32Array(i);this.font=null;this.textRenderingMode=w;this._fillColorSpace=ColorSpace.singletons.gray;this._strokeColorSpace=ColorSpace.singletons.gray;this.patternFillColorSpace=null;this.patternStrokeColorSpace=null}get fillColorSpace(){return this._fillColorSpace}set fillColorSpace(e){this._fillColorSpace=this.patternFillColorSpace=e}get strokeColorSpace(){return this._strokeColorSpace}set strokeColorSpace(e){this._strokeColorSpace=this.patternStrokeColorSpace=e}clone(){return Object.create(this)}}class EvaluatorPreprocessor{static get opMap(){return shadow(this,"opMap",Object.assign(Object.create(null),{w:{id:DA,numArgs:1,variableArgs:!1},J:{id:bA,numArgs:1,variableArgs:!1},j:{id:FA,numArgs:1,variableArgs:!1},M:{id:SA,numArgs:1,variableArgs:!1},d:{id:kA,numArgs:2,variableArgs:!1},ri:{id:RA,numArgs:1,variableArgs:!1},i:{id:NA,numArgs:1,variableArgs:!1},gs:{id:GA,numArgs:1,variableArgs:!1},q:{id:xA,numArgs:0,variableArgs:!1},Q:{id:UA,numArgs:0,variableArgs:!1},cm:{id:MA,numArgs:6,variableArgs:!1},m:{id:LA,numArgs:2,variableArgs:!1},l:{id:HA,numArgs:2,variableArgs:!1},c:{id:JA,numArgs:6,variableArgs:!1},v:{id:YA,numArgs:4,variableArgs:!1},y:{id:vA,numArgs:4,variableArgs:!1},h:{id:KA,numArgs:0,variableArgs:!1},re:{id:TA,numArgs:4,variableArgs:!1},S:{id:qA,numArgs:0,variableArgs:!1},s:{id:OA,numArgs:0,variableArgs:!1},f:{id:PA,numArgs:0,variableArgs:!1},F:{id:PA,numArgs:0,variableArgs:!1},"f*":{id:WA,numArgs:0,variableArgs:!1},B:{id:jA,numArgs:0,variableArgs:!1},"B*":{id:XA,numArgs:0,variableArgs:!1},b:{id:ZA,numArgs:0,variableArgs:!1},"b*":{id:VA,numArgs:0,variableArgs:!1},n:{id:zA,numArgs:0,variableArgs:!1},W:{id:_A,numArgs:0,variableArgs:!1},"W*":{id:$A,numArgs:0,variableArgs:!1},BT:{id:Ae,numArgs:0,variableArgs:!1},ET:{id:ee,numArgs:0,variableArgs:!1},Tc:{id:te,numArgs:1,variableArgs:!1},Tw:{id:ie,numArgs:1,variableArgs:!1},Tz:{id:ae,numArgs:1,variableArgs:!1},TL:{id:se,numArgs:1,variableArgs:!1},Tf:{id:re,numArgs:2,variableArgs:!1},Tr:{id:ne,numArgs:1,variableArgs:!1},Ts:{id:ge,numArgs:1,variableArgs:!1},Td:{id:oe,numArgs:2,variableArgs:!1},TD:{id:Ie,numArgs:2,variableArgs:!1},Tm:{id:ce,numArgs:6,variableArgs:!1},"T*":{id:Ce,numArgs:0,variableArgs:!1},Tj:{id:he,numArgs:1,variableArgs:!1},TJ:{id:Be,numArgs:1,variableArgs:!1},"'":{id:le,numArgs:1,variableArgs:!1},'"':{id:Qe,numArgs:3,variableArgs:!1},d0:{id:Ee,numArgs:2,variableArgs:!1},d1:{id:ue,numArgs:6,variableArgs:!1},CS:{id:de,numArgs:1,variableArgs:!1},cs:{id:fe,numArgs:1,variableArgs:!1},SC:{id:pe,numArgs:4,variableArgs:!0},SCN:{id:me,numArgs:33,variableArgs:!0},sc:{id:ye,numArgs:4,variableArgs:!0},scn:{id:we,numArgs:33,variableArgs:!0},G:{id:De,numArgs:1,variableArgs:!1},g:{id:be,numArgs:1,variableArgs:!1},RG:{id:Fe,numArgs:3,variableArgs:!1},rg:{id:Se,numArgs:3,variableArgs:!1},K:{id:ke,numArgs:4,variableArgs:!1},k:{id:Re,numArgs:4,variableArgs:!1},sh:{id:Ne,numArgs:1,variableArgs:!1},BI:{id:Ge,numArgs:0,variableArgs:!1},ID:{id:xe,numArgs:0,variableArgs:!1},EI:{id:Ue,numArgs:1,variableArgs:!1},Do:{id:Me,numArgs:1,variableArgs:!1},MP:{id:Le,numArgs:1,variableArgs:!1},DP:{id:He,numArgs:2,variableArgs:!1},BMC:{id:Je,numArgs:1,variableArgs:!1},BDC:{id:Ye,numArgs:2,variableArgs:!1},EMC:{id:ve,numArgs:0,variableArgs:!1},BX:{id:Ke,numArgs:0,variableArgs:!1},EX:{id:Te,numArgs:0,variableArgs:!1},BM:null,BD:null,true:null,fa:null,fal:null,fals:null,false:null,nu:null,nul:null,null:null}))}static MAX_INVALID_PATH_OPS=10;constructor(e,t,i=new StateManager){this.parser=new Parser({lexer:new Lexer(e,EvaluatorPreprocessor.opMap),xref:t});this.stateManager=i;this.nonProcessedArgs=[];this._isPathOp=!1;this._numInvalidPathOPS=0}get savedStatesDepth(){return this.stateManager.stateStack.length}read(e){let t=e.args;for(;;){const i=this.parser.getObj();if(i instanceof Cmd){const a=i.cmd,s=EvaluatorPreprocessor.opMap[a];if(!s){warn(`Unknown command "${a}".`);continue}const r=s.id,n=s.numArgs;let g=null!==t?t.length:0;this._isPathOp||(this._numInvalidPathOPS=0);this._isPathOp=r>=LA&&r<=zA;if(s.variableArgs)g>n&&info(`Command ${a}: expected [0, ${n}] args, but received ${g} args.`);else{if(g!==n){const e=this.nonProcessedArgs;for(;g>n;){e.push(t.shift());g--}for(;g<n&&0!==e.length;){null===t&&(t=[]);t.unshift(e.pop());g++}}if(g<n){const e=`command ${a}: expected ${n} args, but received ${g} args.`;if(this._isPathOp&&++this._numInvalidPathOPS>EvaluatorPreprocessor.MAX_INVALID_PATH_OPS)throw new FormatError(`Invalid ${e}`);warn(`Skipping ${e}`);null!==t&&(t.length=0);continue}}this.preprocessCommand(r,t);e.fn=r;e.args=t;return!0}if(i===wt)return!1;if(null!==i){null===t&&(t=[]);t.push(i);if(t.length>33)throw new FormatError("Too many arguments")}}}preprocessCommand(e,t){switch(0|e){case xA:this.stateManager.save();break;case UA:this.stateManager.restore();break;case MA:this.stateManager.transform(t)}}}class DefaultAppearanceEvaluator extends EvaluatorPreprocessor{constructor(e){super(new StringStream(e))}parse(){const e={fn:0,args:[]},t={fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3)};try{for(;;){e.args.length=0;if(!this.read(e))break;if(0!==this.savedStatesDepth)continue;const{fn:i,args:a}=e;switch(0|i){case re:const[e,i]=a;e instanceof Name&&(t.fontName=e.name);"number"==typeof i&&i>0&&(t.fontSize=i);break;case Se:ColorSpace.singletons.rgb.getRgbItem(a,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(a,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(a,0,t.fontColor,0)}}}catch(e){warn(`parseDefaultAppearance - ignoring errors: "${e}".`)}return t}}function parseDefaultAppearance(e){return new DefaultAppearanceEvaluator(e).parse()}class AppearanceStreamEvaluator extends EvaluatorPreprocessor{constructor(e,t,i){super(e);this.stream=e;this.evaluatorOptions=t;this.xref=i;this.resources=e.dict?.get("Resources")}parse(){const e={fn:0,args:[]};let t={scaleFactor:1,fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3),fillColorSpace:ColorSpace.singletons.gray},i=!1;const a=[];try{for(;;){e.args.length=0;if(i||!this.read(e))break;const{fn:s,args:r}=e;switch(0|s){case xA:a.push({scaleFactor:t.scaleFactor,fontSize:t.fontSize,fontName:t.fontName,fontColor:t.fontColor.slice(),fillColorSpace:t.fillColorSpace});break;case UA:t=a.pop()||t;break;case ce:t.scaleFactor*=Math.hypot(r[0],r[1]);break;case re:const[e,s]=r;e instanceof Name&&(t.fontName=e.name);"number"==typeof s&&s>0&&(t.fontSize=s*t.scaleFactor);break;case fe:t.fillColorSpace=ColorSpace.parse({cs:r[0],xref:this.xref,resources:this.resources,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:this._localColorSpaceCache});break;case ye:t.fillColorSpace.getRgbItem(r,0,t.fontColor,0);break;case Se:ColorSpace.singletons.rgb.getRgbItem(r,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(r,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(r,0,t.fontColor,0);break;case he:case Be:case le:case Qe:i=!0}}}catch(e){warn(`parseAppearanceStream - ignoring errors: "${e}".`)}this.stream.reset();delete t.scaleFactor;delete t.fillColorSpace;return t}get _localColorSpaceCache(){return shadow(this,"_localColorSpaceCache",new LocalColorSpaceCache)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.evaluatorOptions.isEvalSupported}))}}function getPdfColor(e,t){if(e[0]===e[1]&&e[1]===e[2]){return`${numberToString(e[0]/255)} ${t?"g":"G"}`}return Array.from(e,(e=>numberToString(e/255))).join(" ")+" "+(t?"rg":"RG")}class FakeUnicodeFont{constructor(e,t){this.xref=e;this.widths=null;this.firstChar=1/0;this.lastChar=-1/0;this.fontFamily=t;const i=new OffscreenCanvas(1,1);this.ctxMeasure=i.getContext("2d",{willReadFrequently:!0});FakeUnicodeFont._fontNameId||(FakeUnicodeFont._fontNameId=1);this.fontName=Name.get(`InvalidPDFjsFont_${t}_${FakeUnicodeFont._fontNameId++}`)}get fontDescriptorRef(){if(!FakeUnicodeFont._fontDescriptorRef){const e=new Dict(this.xref);e.set("Type",Name.get("FontDescriptor"));e.set("FontName",this.fontName);e.set("FontFamily","MyriadPro Regular");e.set("FontBBox",[0,0,0,0]);e.set("FontStretch",Name.get("Normal"));e.set("FontWeight",400);e.set("ItalicAngle",0);FakeUnicodeFont._fontDescriptorRef=this.xref.getNewPersistentRef(e)}return FakeUnicodeFont._fontDescriptorRef}get descendantFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("CIDFontType0"));e.set("CIDToGIDMap",Name.get("Identity"));e.set("FirstChar",this.firstChar);e.set("LastChar",this.lastChar);e.set("FontDescriptor",this.fontDescriptorRef);e.set("DW",1e3);const t=[],i=[...this.widths.entries()].sort();let a=null,s=null;for(const[e,r]of i)if(a)if(e===a+s.length)s.push(r);else{t.push(a,s);a=e;s=[r]}else{a=e;s=[r]}a&&t.push(a,s);e.set("W",t);const r=new Dict(this.xref);r.set("Ordering","Identity");r.set("Registry","Adobe");r.set("Supplement",0);e.set("CIDSystemInfo",r);return this.xref.getNewPersistentRef(e)}get baseFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type0"));e.set("Encoding",Name.get("Identity-H"));e.set("DescendantFonts",[this.descendantFontRef]);e.set("ToUnicode",Name.get("Identity-H"));return this.xref.getNewPersistentRef(e)}get resources(){const e=new Dict(this.xref),t=new Dict(this.xref);t.set(this.fontName.name,this.baseFontRef);e.set("Font",t);return e}_createContext(){this.widths=new Map;this.ctxMeasure.font=`1000px ${this.fontFamily}`;return this.ctxMeasure}createFontResources(e){const t=this._createContext();for(const i of e.split(/\r\n?|\n/))for(const e of i.split("")){const i=e.charCodeAt(0);if(this.widths.has(i))continue;const a=t.measureText(e),s=Math.ceil(a.width);this.widths.set(i,s);this.firstChar=Math.min(i,this.firstChar);this.lastChar=Math.max(i,this.lastChar)}return this.resources}static getFirstPositionInfo(e,t,i){const[a,n,g,o]=e;let c=g-a,C=o-n;t%180!=0&&([c,C]=[C,c]);const h=s*i;return{coords:[0,C+r*i-h],bbox:[0,0,c,C],matrix:0!==t?getRotationMatrix(t,C,h):void 0}}createAppearance(e,t,i,a,n,g){const o=this._createContext(),c=[];let C=-1/0;for(const t of e.split(/\r\n?|\n/)){c.push(t);const e=o.measureText(t).width;C=Math.max(C,e);for(const e of codePointIter(t)){const t=String.fromCodePoint(e);let i=this.widths.get(e);if(void 0===i){const a=o.measureText(t);i=Math.ceil(a.width);this.widths.set(e,i);this.firstChar=Math.min(e,this.firstChar);this.lastChar=Math.max(e,this.lastChar)}}}C*=a/1e3;const[h,l,Q,E]=t;let u=Q-h,d=E-l;i%180!=0&&([u,d]=[d,u]);let f=1;C>u&&(f=u/C);let p=1;const m=s*a,y=r*a,w=m*c.length;w>d&&(p=d/w);const D=a*Math.min(f,p),b=["q",`0 0 ${numberToString(u)} ${numberToString(d)} re W n`,"BT",`1 0 0 1 0 ${numberToString(d+y)} Tm 0 Tc ${getPdfColor(n,!0)}`,`/${this.fontName.name} ${numberToString(D)} Tf`],{resources:F}=this;if(1!==(g="number"==typeof g&&g>=0&&g<=1?g:1)){b.push("/R0 gs");const e=new Dict(this.xref),t=new Dict(this.xref);t.set("ca",g);t.set("CA",g);t.set("Type",Name.get("ExtGState"));e.set("R0",t);F.set("ExtGState",e)}const S=numberToString(m);for(const e of c)b.push(`0 -${S} Td <${stringToUTF16HexString(e)}> Tj`);b.push("ET","Q");const k=b.join("\n"),R=new Dict(this.xref);R.set("Subtype",Name.get("Form"));R.set("Type",Name.get("XObject"));R.set("BBox",[0,0,u,d]);R.set("Length",k.length);R.set("Resources",F);if(i){const e=getRotationMatrix(i,u,d);R.set("Matrix",e)}const N=new StringStream(k);N.dict=R;return N}}class NameOrNumberTree{constructor(e,t,i){this.root=e;this.xref=t;this._type=i}getAll(){const e=new Map;if(!this.root)return e;const t=this.xref,i=new RefSet;i.put(this.root);const a=[this.root];for(;a.length>0;){const s=t.fetchIfRef(a.shift());if(!(s instanceof Dict))continue;if(s.has("Kids")){const e=s.get("Kids");if(!Array.isArray(e))continue;for(const t of e){if(i.has(t))throw new FormatError(`Duplicate entry in "${this._type}" tree.`);a.push(t);i.put(t)}continue}const r=s.get(this._type);if(Array.isArray(r))for(let i=0,a=r.length;i<a;i+=2)e.set(t.fetchIfRef(r[i]),t.fetchIfRef(r[i+1]))}return e}getRaw(e){if(!this.root)return null;const t=this.xref;let i=t.fetchIfRef(this.root),a=0;for(;i.has("Kids");){if(++a>10){warn(`Search depth limit reached for "${this._type}" tree.`);return null}const s=i.get("Kids");if(!Array.isArray(s))return null;let r=0,n=s.length-1;for(;r<=n;){const a=r+n>>1,g=t.fetchIfRef(s[a]),o=g.get("Limits");if(e<t.fetchIfRef(o[0]))n=a-1;else{if(!(e>t.fetchIfRef(o[1]))){i=g;break}r=a+1}}if(r>n)return null}const s=i.get(this._type);if(Array.isArray(s)){let i=0,a=s.length-2;for(;i<=a;){const r=i+a>>1,n=r+(1&r),g=t.fetchIfRef(s[n]);if(e<g)a=n-2;else{if(!(e>g))return s[n+1];i=n+2}}}return null}get(e){return this.xref.fetchIfRef(this.getRaw(e))}}class NameTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Names")}}class NumberTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Nums")}}function clearGlobalCaches(){!function clearPatternCaches(){Fa=Object.create(null)}();!function clearPrimitiveCaches(){Dt=Object.create(null);bt=Object.create(null);Ft=Object.create(null)}();!function clearUnicodeCaches(){Ki.clear()}();JpxImage.cleanup()}function pickPlatformItem(e){return e instanceof Dict?e.has("UF")?e.get("UF"):e.has("F")?e.get("F"):e.has("Unix")?e.get("Unix"):e.has("Mac")?e.get("Mac"):e.has("DOS")?e.get("DOS"):null:null}class FileSpec{#S=!1;constructor(e,t,i=!1){if(e instanceof Dict){this.xref=t;this.root=e;e.has("FS")&&(this.fs=e.get("FS"));e.has("RF")&&warn("Related file specifications are not supported");i||(e.has("EF")?this.#S=!0:warn("Non-embedded file specifications are not supported"))}}get filename(){let e="";const t=pickPlatformItem(this.root);t&&"string"==typeof t&&(e=stringToPDFString(t).replaceAll("\\\\","\\").replaceAll("\\/","/").replaceAll("\\","/"));return shadow(this,"filename",e||"unnamed")}get content(){if(!this.#S)return null;this._contentRef||=pickPlatformItem(this.root?.get("EF"));let e=null;if(this._contentRef){const t=this.xref.fetchIfRef(this._contentRef);t instanceof BaseStream?e=t.getBytes():warn("Embedded file specification points to non-existing/invalid content")}else warn("Embedded file specification does not have any content");return e}get description(){let e="";const t=this.root?.get("Desc");t&&"string"==typeof t&&(e=stringToPDFString(t));return shadow(this,"description",e)}get serializable(){return{rawFilename:this.filename,filename:(e=this.filename,e.substring(e.lastIndexOf("/")+1)),content:this.content,description:this.description};var e}}const Us=0,Ms=-2,Ls=-3,Hs=-4,Js=-5,Ys=-6,vs=-9;function isWhitespace(e,t){const i=e[t];return" "===i||"\n"===i||"\r"===i||"\t"===i}class XMLParserBase{_resolveEntities(e){return e.replaceAll(/&([^;]+);/g,((e,t)=>{if("#x"===t.substring(0,2))return String.fromCodePoint(parseInt(t.substring(2),16));if("#"===t.substring(0,1))return String.fromCodePoint(parseInt(t.substring(1),10));switch(t){case"lt":return"<";case"gt":return">";case"amp":return"&";case"quot":return'"';case"apos":return"'"}return this.onResolveEntity(t)}))}_parseContent(e,t){const i=[];let a=t;function skipWs(){for(;a<e.length&&isWhitespace(e,a);)++a}for(;a<e.length&&!isWhitespace(e,a)&&">"!==e[a]&&"/"!==e[a];)++a;const s=e.substring(t,a);skipWs();for(;a<e.length&&">"!==e[a]&&"/"!==e[a]&&"?"!==e[a];){skipWs();let t="",s="";for(;a<e.length&&!isWhitespace(e,a)&&"="!==e[a];){t+=e[a];++a}skipWs();if("="!==e[a])return null;++a;skipWs();const r=e[a];if('"'!==r&&"'"!==r)return null;const n=e.indexOf(r,++a);if(n<0)return null;s=e.substring(a,n);i.push({name:t,value:this._resolveEntities(s)});a=n+1;skipWs()}return{name:s,attributes:i,parsed:a-t}}_parseProcessingInstruction(e,t){let i=t;for(;i<e.length&&!isWhitespace(e,i)&&">"!==e[i]&&"?"!==e[i]&&"/"!==e[i];)++i;const a=e.substring(t,i);!function skipWs(){for(;i<e.length&&isWhitespace(e,i);)++i}();const s=i;for(;i<e.length&&("?"!==e[i]||">"!==e[i+1]);)++i;return{name:a,value:e.substring(s,i),parsed:i-t}}parseXml(e){let t=0;for(;t<e.length;){let i=t;if("<"===e[t]){++i;let t;switch(e[i]){case"/":++i;t=e.indexOf(">",i);if(t<0){this.onError(vs);return}this.onEndElement(e.substring(i,t));i=t+1;break;case"?":++i;const a=this._parseProcessingInstruction(e,i);if("?>"!==e.substring(i+a.parsed,i+a.parsed+2)){this.onError(Ls);return}this.onPi(a.name,a.value);i+=a.parsed+2;break;case"!":if("--"===e.substring(i+1,i+3)){t=e.indexOf("--\x3e",i+3);if(t<0){this.onError(Js);return}this.onComment(e.substring(i+3,t));i=t+3}else if("[CDATA["===e.substring(i+1,i+8)){t=e.indexOf("]]>",i+8);if(t<0){this.onError(Ms);return}this.onCdata(e.substring(i+8,t));i=t+3}else{if("DOCTYPE"!==e.substring(i+1,i+8)){this.onError(Ys);return}{const a=e.indexOf("[",i+8);let s=!1;t=e.indexOf(">",i+8);if(t<0){this.onError(Hs);return}if(a>0&&t>a){t=e.indexOf("]>",i+8);if(t<0){this.onError(Hs);return}s=!0}const r=e.substring(i+8,t+(s?1:0));this.onDoctype(r);i=t+(s?2:1)}}break;default:const s=this._parseContent(e,i);if(null===s){this.onError(Ys);return}let r=!1;if("/>"===e.substring(i+s.parsed,i+s.parsed+2))r=!0;else if(">"!==e.substring(i+s.parsed,i+s.parsed+1)){this.onError(vs);return}this.onBeginElement(s.name,s.attributes,r);i+=s.parsed+(r?2:1)}}else{for(;i<e.length&&"<"!==e[i];)i++;const a=e.substring(t,i);this.onText(this._resolveEntities(a))}t=i}}onResolveEntity(e){return`&${e};`}onPi(e,t){}onComment(e){}onCdata(e){}onDoctype(e){}onText(e){}onBeginElement(e,t,i){}onEndElement(e){}onError(e){}}class SimpleDOMNode{constructor(e,t){this.nodeName=e;this.nodeValue=t;Object.defineProperty(this,"parentNode",{value:null,writable:!0})}get firstChild(){return this.childNodes?.[0]}get nextSibling(){const e=this.parentNode.childNodes;if(!e)return;const t=e.indexOf(this);return-1!==t?e[t+1]:void 0}get textContent(){return this.childNodes?this.childNodes.map((function(e){return e.textContent})).join(""):this.nodeValue||""}get children(){return this.childNodes||[]}hasChildNodes(){return this.childNodes?.length>0}searchNode(e,t){if(t>=e.length)return this;const i=e[t];if(i.name.startsWith("#")&&t<e.length-1)return this.searchNode(e,t+1);const a=[];let s=this;for(;;){if(i.name===s.nodeName){if(0!==i.pos){if(0===a.length)return null;{const[r]=a.pop();let n=0;for(const a of r.childNodes)if(i.name===a.nodeName){if(n===i.pos)return a.searchNode(e,t+1);n++}return s.searchNode(e,t+1)}}{const i=s.searchNode(e,t+1);if(null!==i)return i}}if(s.childNodes?.length>0){a.push([s,0]);s=s.childNodes[0]}else{if(0===a.length)return null;for(;0!==a.length;){const[e,t]=a.pop(),i=t+1;if(i<e.childNodes.length){a.push([e,i]);s=e.childNodes[i];break}}if(0===a.length)return null}}}dump(e){if("#text"!==this.nodeName){e.push(`<${this.nodeName}`);if(this.attributes)for(const t of this.attributes)e.push(` ${t.name}="${encodeToXmlString(t.value)}"`);if(this.hasChildNodes()){e.push(">");for(const t of this.childNodes)t.dump(e);e.push(`</${this.nodeName}>`)}else this.nodeValue?e.push(`>${encodeToXmlString(this.nodeValue)}</${this.nodeName}>`):e.push("/>")}else e.push(encodeToXmlString(this.nodeValue))}}class SimpleXMLParser extends XMLParserBase{constructor({hasAttributes:e=!1,lowerCaseName:t=!1}){super();this._currentFragment=null;this._stack=null;this._errorCode=Us;this._hasAttributes=e;this._lowerCaseName=t}parseFromString(e){this._currentFragment=[];this._stack=[];this._errorCode=Us;this.parseXml(e);if(this._errorCode!==Us)return;const[t]=this._currentFragment;return t?{documentElement:t}:void 0}onText(e){if(function isWhitespaceString(e){for(let t=0,i=e.length;t<i;t++)if(!isWhitespace(e,t))return!1;return!0}(e))return;const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onCdata(e){const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onBeginElement(e,t,i){this._lowerCaseName&&(e=e.toLowerCase());const a=new SimpleDOMNode(e);a.childNodes=[];this._hasAttributes&&(a.attributes=t);this._currentFragment.push(a);if(!i){this._stack.push(this._currentFragment);this._currentFragment=a.childNodes}}onEndElement(e){this._currentFragment=this._stack.pop()||[];const t=this._currentFragment.at(-1);if(!t)return null;for(const e of t.childNodes)e.parentNode=t;return t}onError(e){this._errorCode=e}}class MetadataParser{constructor(e){e=this._repair(e);const t=new SimpleXMLParser({lowerCaseName:!0}).parseFromString(e);this._metadataMap=new Map;this._data=e;t&&this._parse(t)}_repair(e){return e.replace(/^[^<]+/,"").replaceAll(/>\\376\\377([^<]+)/g,(function(e,t){const i=t.replaceAll(/\\([0-3])([0-7])([0-7])/g,(function(e,t,i,a){return String.fromCharCode(64*t+8*i+1*a)})).replaceAll(/&(amp|apos|gt|lt|quot);/g,(function(e,t){switch(t){case"amp":return"&";case"apos":return"'";case"gt":return">";case"lt":return"<";case"quot":return'"'}throw new Error(`_repair: ${t} isn't defined.`)})),a=[">"];for(let e=0,t=i.length;e<t;e+=2){const t=256*i.charCodeAt(e)+i.charCodeAt(e+1);t>=32&&t<127&&60!==t&&62!==t&&38!==t?a.push(String.fromCharCode(t)):a.push("&#x"+(65536+t).toString(16).substring(1)+";")}return a.join("")}))}_getSequence(e){const t=e.nodeName;return"rdf:bag"!==t&&"rdf:seq"!==t&&"rdf:alt"!==t?null:e.childNodes.filter((e=>"rdf:li"===e.nodeName))}_parseArray(e){if(!e.hasChildNodes())return;const[t]=e.childNodes,i=this._getSequence(t)||[];this._metadataMap.set(e.nodeName,i.map((e=>e.textContent.trim())))}_parse(e){let t=e.documentElement;if("rdf:rdf"!==t.nodeName){t=t.firstChild;for(;t&&"rdf:rdf"!==t.nodeName;)t=t.nextSibling}if(t&&"rdf:rdf"===t.nodeName&&t.hasChildNodes())for(const e of t.childNodes)if("rdf:description"===e.nodeName)for(const t of e.childNodes){const e=t.nodeName;switch(e){case"#text":continue;case"dc:creator":case"dc:subject":this._parseArray(t);continue}this._metadataMap.set(e,t.textContent.trim())}}get serializable(){return{parsedData:this._metadataMap,rawData:this._data}}}class DecryptStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.decrypt=i;this.nextChunk=null;this.initialized=!1}readBlock(){let e;if(this.initialized)e=this.nextChunk;else{e=this.str.getBytes(512);this.initialized=!0}if(!e||0===e.length){this.eof=!0;return}this.nextChunk=this.str.getBytes(512);const t=this.nextChunk?.length>0;e=(0,this.decrypt)(e,!t);const i=this.bufferLength,a=i+e.length;this.ensureBuffer(a).set(e,i);this.bufferLength=a}}class ARCFourCipher{constructor(e){this.a=0;this.b=0;const t=new Uint8Array(256),i=e.length;for(let e=0;e<256;++e)t[e]=e;for(let a=0,s=0;a<256;++a){const r=t[a];s=s+r+e[a%i]&255;t[a]=t[s];t[s]=r}this.s=t}encryptBlock(e){let t=this.a,i=this.b;const a=this.s,s=e.length,r=new Uint8Array(s);for(let n=0;n<s;++n){t=t+1&255;const s=a[t];i=i+s&255;const g=a[i];a[t]=g;a[i]=s;r[n]=e[n]^a[s+g&255]}this.a=t;this.b=i;return r}decryptBlock(e){return this.encryptBlock(e)}encrypt(e){return this.encryptBlock(e)}}const Ks=function calculateMD5Closure(){const e=new Uint8Array([7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21]),t=new Int32Array([-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]);return function hash(i,a,s){let r=1732584193,n=-271733879,g=-1732584194,o=271733878;const c=s+72&-64,C=new Uint8Array(c);let h,l;for(h=0;h<s;++h)C[h]=i[a++];C[h++]=128;const Q=c-8;for(;h<Q;)C[h++]=0;C[h++]=s<<3&255;C[h++]=s>>5&255;C[h++]=s>>13&255;C[h++]=s>>21&255;C[h++]=s>>>29&255;C[h++]=0;C[h++]=0;C[h++]=0;const E=new Int32Array(16);for(h=0;h<c;){for(l=0;l<16;++l,h+=4)E[l]=C[h]|C[h+1]<<8|C[h+2]<<16|C[h+3]<<24;let i,a,s=r,c=n,Q=g,u=o;for(l=0;l<64;++l){if(l<16){i=c&Q|~c&u;a=l}else if(l<32){i=u&c|~u&Q;a=5*l+1&15}else if(l<48){i=c^Q^u;a=3*l+5&15}else{i=Q^(c|~u);a=7*l&15}const r=u,n=s+i+t[l]+E[a]|0,g=e[l];u=Q;Q=c;c=c+(n<<g|n>>>32-g)|0;s=r}r=r+s|0;n=n+c|0;g=g+Q|0;o=o+u|0}return new Uint8Array([255&r,r>>8&255,r>>16&255,r>>>24&255,255&n,n>>8&255,n>>16&255,n>>>24&255,255&g,g>>8&255,g>>16&255,g>>>24&255,255&o,o>>8&255,o>>16&255,o>>>24&255])}}();class Word64{constructor(e,t){this.high=0|e;this.low=0|t}and(e){this.high&=e.high;this.low&=e.low}xor(e){this.high^=e.high;this.low^=e.low}or(e){this.high|=e.high;this.low|=e.low}shiftRight(e){if(e>=32){this.low=this.high>>>e-32|0;this.high=0}else{this.low=this.low>>>e|this.high<<32-e;this.high=this.high>>>e|0}}shiftLeft(e){if(e>=32){this.high=this.low<<e-32;this.low=0}else{this.high=this.high<<e|this.low>>>32-e;this.low<<=e}}rotateRight(e){let t,i;if(32&e){i=this.low;t=this.high}else{t=this.low;i=this.high}e&=31;this.low=t>>>e|i<<32-e;this.high=i>>>e|t<<32-e}not(){this.high=~this.high;this.low=~this.low}add(e){const t=(this.low>>>0)+(e.low>>>0);let i=(this.high>>>0)+(e.high>>>0);t>4294967295&&(i+=1);this.low=0|t;this.high=0|i}copyTo(e,t){e[t]=this.high>>>24&255;e[t+1]=this.high>>16&255;e[t+2]=this.high>>8&255;e[t+3]=255&this.high;e[t+4]=this.low>>>24&255;e[t+5]=this.low>>16&255;e[t+6]=this.low>>8&255;e[t+7]=255&this.low}assign(e){this.high=e.high;this.low=e.low}}const Ts=function calculateSHA256Closure(){function rotr(e,t){return e>>>t|e<<32-t}function ch(e,t,i){return e&t^~e&i}function maj(e,t,i){return e&t^e&i^t&i}function sigma(e){return rotr(e,2)^rotr(e,13)^rotr(e,22)}function sigmaPrime(e){return rotr(e,6)^rotr(e,11)^rotr(e,25)}function littleSigma(e){return rotr(e,7)^rotr(e,18)^e>>>3}const e=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];return function hash(t,i,a){let s=1779033703,r=3144134277,n=1013904242,g=2773480762,o=1359893119,c=2600822924,C=528734635,h=1541459225;const l=64*Math.ceil((a+9)/64),Q=new Uint8Array(l);let E,u;for(E=0;E<a;++E)Q[E]=t[i++];Q[E++]=128;const d=l-8;for(;E<d;)Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=a>>>29&255;Q[E++]=a>>21&255;Q[E++]=a>>13&255;Q[E++]=a>>5&255;Q[E++]=a<<3&255;const f=new Uint32Array(64);for(E=0;E<l;){for(u=0;u<16;++u){f[u]=Q[E]<<24|Q[E+1]<<16|Q[E+2]<<8|Q[E+3];E+=4}for(u=16;u<64;++u)f[u]=(rotr(p=f[u-2],17)^rotr(p,19)^p>>>10)+f[u-7]+littleSigma(f[u-15])+f[u-16]|0;let t,i,a=s,l=r,d=n,m=g,y=o,w=c,D=C,b=h;for(u=0;u<64;++u){t=b+sigmaPrime(y)+ch(y,w,D)+e[u]+f[u];i=sigma(a)+maj(a,l,d);b=D;D=w;w=y;y=m+t|0;m=d;d=l;l=a;a=t+i|0}s=s+a|0;r=r+l|0;n=n+d|0;g=g+m|0;o=o+y|0;c=c+w|0;C=C+D|0;h=h+b|0}var p;return new Uint8Array([s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,n>>24&255,n>>16&255,n>>8&255,255&n,g>>24&255,g>>16&255,g>>8&255,255&g,o>>24&255,o>>16&255,o>>8&255,255&o,c>>24&255,c>>16&255,c>>8&255,255&c,C>>24&255,C>>16&255,C>>8&255,255&C,h>>24&255,h>>16&255,h>>8&255,255&h])}}(),qs=function calculateSHA512Closure(){function ch(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.not();s.and(a);e.xor(s)}function maj(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.and(a);e.xor(s);s.assign(i);s.and(a);e.xor(s)}function sigma(e,t,i){e.assign(t);e.rotateRight(28);i.assign(t);i.rotateRight(34);e.xor(i);i.assign(t);i.rotateRight(39);e.xor(i)}function sigmaPrime(e,t,i){e.assign(t);e.rotateRight(14);i.assign(t);i.rotateRight(18);e.xor(i);i.assign(t);i.rotateRight(41);e.xor(i)}function littleSigma(e,t,i){e.assign(t);e.rotateRight(1);i.assign(t);i.rotateRight(8);e.xor(i);i.assign(t);i.shiftRight(7);e.xor(i)}function littleSigmaPrime(e,t,i){e.assign(t);e.rotateRight(19);i.assign(t);i.rotateRight(61);e.xor(i);i.assign(t);i.shiftRight(6);e.xor(i)}const e=[new Word64(1116352408,3609767458),new Word64(1899447441,602891725),new Word64(3049323471,3964484399),new Word64(3921009573,2173295548),new Word64(961987163,4081628472),new Word64(1508970993,3053834265),new Word64(2453635748,2937671579),new Word64(2870763221,3664609560),new Word64(3624381080,2734883394),new Word64(310598401,1164996542),new Word64(607225278,1323610764),new Word64(1426881987,3590304994),new Word64(1925078388,4068182383),new Word64(2162078206,991336113),new Word64(2614888103,633803317),new Word64(3248222580,3479774868),new Word64(3835390401,2666613458),new Word64(4022224774,944711139),new Word64(264347078,2341262773),new Word64(604807628,2007800933),new Word64(770255983,1495990901),new Word64(1249150122,1856431235),new Word64(1555081692,3175218132),new Word64(1996064986,2198950837),new Word64(2554220882,3999719339),new Word64(2821834349,766784016),new Word64(2952996808,2566594879),new Word64(3210313671,3203337956),new Word64(3336571891,1034457026),new Word64(3584528711,2466948901),new Word64(113926993,3758326383),new Word64(338241895,168717936),new Word64(666307205,1188179964),new Word64(773529912,1546045734),new Word64(1294757372,1522805485),new Word64(1396182291,2643833823),new Word64(1695183700,2343527390),new Word64(1986661051,1014477480),new Word64(2177026350,1206759142),new Word64(2456956037,344077627),new Word64(2730485921,1290863460),new Word64(2820302411,3158454273),new Word64(3259730800,3505952657),new Word64(3345764771,106217008),new Word64(3516065817,3606008344),new Word64(3600352804,1432725776),new Word64(4094571909,1467031594),new Word64(275423344,851169720),new Word64(430227734,3100823752),new Word64(506948616,1363258195),new Word64(659060556,3750685593),new Word64(883997877,3785050280),new Word64(958139571,3318307427),new Word64(1322822218,3812723403),new Word64(1537002063,2003034995),new Word64(1747873779,3602036899),new Word64(1955562222,1575990012),new Word64(2024104815,1125592928),new Word64(2227730452,2716904306),new Word64(2361852424,442776044),new Word64(2428436474,593698344),new Word64(2756734187,3733110249),new Word64(3204031479,2999351573),new Word64(3329325298,3815920427),new Word64(3391569614,3928383900),new Word64(3515267271,566280711),new Word64(3940187606,3454069534),new Word64(4118630271,4000239992),new Word64(116418474,1914138554),new Word64(174292421,2731055270),new Word64(289380356,3203993006),new Word64(460393269,320620315),new Word64(685471733,587496836),new Word64(852142971,1086792851),new Word64(1017036298,365543100),new Word64(1126000580,2618297676),new Word64(1288033470,3409855158),new Word64(1501505948,4234509866),new Word64(1607167915,987167468),new Word64(1816402316,1246189591)];return function hash(t,i,a,s=!1){let r,n,g,o,c,C,h,l;if(s){r=new Word64(3418070365,3238371032);n=new Word64(1654270250,914150663);g=new Word64(2438529370,812702999);o=new Word64(355462360,4144912697);c=new Word64(1731405415,4290775857);C=new Word64(2394180231,1750603025);h=new Word64(3675008525,1694076839);l=new Word64(1203062813,3204075428)}else{r=new Word64(1779033703,4089235720);n=new Word64(3144134277,2227873595);g=new Word64(1013904242,4271175723);o=new Word64(2773480762,1595750129);c=new Word64(1359893119,2917565137);C=new Word64(2600822924,725511199);h=new Word64(528734635,4215389547);l=new Word64(1541459225,327033209)}const Q=128*Math.ceil((a+17)/128),E=new Uint8Array(Q);let u,d;for(u=0;u<a;++u)E[u]=t[i++];E[u++]=128;const f=Q-16;for(;u<f;)E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=a>>>29&255;E[u++]=a>>21&255;E[u++]=a>>13&255;E[u++]=a>>5&255;E[u++]=a<<3&255;const p=new Array(80);for(u=0;u<80;u++)p[u]=new Word64(0,0);let m=new Word64(0,0),y=new Word64(0,0),w=new Word64(0,0),D=new Word64(0,0),b=new Word64(0,0),F=new Word64(0,0),S=new Word64(0,0),k=new Word64(0,0);const R=new Word64(0,0),N=new Word64(0,0),G=new Word64(0,0),x=new Word64(0,0);let U,M;for(u=0;u<Q;){for(d=0;d<16;++d){p[d].high=E[u]<<24|E[u+1]<<16|E[u+2]<<8|E[u+3];p[d].low=E[u+4]<<24|E[u+5]<<16|E[u+6]<<8|E[u+7];u+=8}for(d=16;d<80;++d){U=p[d];littleSigmaPrime(U,p[d-2],x);U.add(p[d-7]);littleSigma(G,p[d-15],x);U.add(G);U.add(p[d-16])}m.assign(r);y.assign(n);w.assign(g);D.assign(o);b.assign(c);F.assign(C);S.assign(h);k.assign(l);for(d=0;d<80;++d){R.assign(k);sigmaPrime(G,b,x);R.add(G);ch(G,b,F,S,x);R.add(G);R.add(e[d]);R.add(p[d]);sigma(N,m,x);maj(G,m,y,w,x);N.add(G);U=k;k=S;S=F;F=b;D.add(R);b=D;D=w;w=y;y=m;U.assign(R);U.add(N);m=U}r.add(m);n.add(y);g.add(w);o.add(D);c.add(b);C.add(F);h.add(S);l.add(k)}if(s){M=new Uint8Array(48);r.copyTo(M,0);n.copyTo(M,8);g.copyTo(M,16);o.copyTo(M,24);c.copyTo(M,32);C.copyTo(M,40)}else{M=new Uint8Array(64);r.copyTo(M,0);n.copyTo(M,8);g.copyTo(M,16);o.copyTo(M,24);c.copyTo(M,32);C.copyTo(M,40);h.copyTo(M,48);l.copyTo(M,56)}return M}}();class NullCipher{decryptBlock(e){return e}encrypt(e){return e}}class AESBaseCipher{constructor(){this._s=new Uint8Array([99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22]);this._inv_s=new Uint8Array([82,9,106,213,48,54,165,56,191,64,163,158,129,243,215,251,124,227,57,130,155,47,255,135,52,142,67,68,196,222,233,203,84,123,148,50,166,194,35,61,238,76,149,11,66,250,195,78,8,46,161,102,40,217,36,178,118,91,162,73,109,139,209,37,114,248,246,100,134,104,152,22,212,164,92,204,93,101,182,146,108,112,72,80,253,237,185,218,94,21,70,87,167,141,157,132,144,216,171,0,140,188,211,10,247,228,88,5,184,179,69,6,208,44,30,143,202,63,15,2,193,175,189,3,1,19,138,107,58,145,17,65,79,103,220,234,151,242,207,206,240,180,230,115,150,172,116,34,231,173,53,133,226,249,55,232,28,117,223,110,71,241,26,113,29,41,197,137,111,183,98,14,170,24,190,27,252,86,62,75,198,210,121,32,154,219,192,254,120,205,90,244,31,221,168,51,136,7,199,49,177,18,16,89,39,128,236,95,96,81,127,169,25,181,74,13,45,229,122,159,147,201,156,239,160,224,59,77,174,42,245,176,200,235,187,60,131,83,153,97,23,43,4,126,186,119,214,38,225,105,20,99,85,33,12,125]);this._mix=new Uint32Array([0,235474187,470948374,303765277,941896748,908933415,607530554,708780849,1883793496,2118214995,1817866830,1649639237,1215061108,1181045119,1417561698,1517767529,3767586992,4003061179,4236429990,4069246893,3635733660,3602770327,3299278474,3400528769,2430122216,2664543715,2362090238,2193862645,2835123396,2801107407,3035535058,3135740889,3678124923,3576870512,3341394285,3374361702,3810496343,3977675356,4279080257,4043610186,2876494627,2776292904,3076639029,3110650942,2472011535,2640243204,2403728665,2169303058,1001089995,899835584,666464733,699432150,59727847,226906860,530400753,294930682,1273168787,1172967064,1475418501,1509430414,1942435775,2110667444,1876241833,1641816226,2910219766,2743034109,2976151520,3211623147,2505202138,2606453969,2302690252,2269728455,3711829422,3543599269,3240894392,3475313331,3843699074,3943906441,4178062228,4144047775,1306967366,1139781709,1374988112,1610459739,1975683434,2076935265,1775276924,1742315127,1034867998,866637845,566021896,800440835,92987698,193195065,429456164,395441711,1984812685,2017778566,1784663195,1683407248,1315562145,1080094634,1383856311,1551037884,101039829,135050206,437757123,337553864,1042385657,807962610,573804783,742039012,2531067453,2564033334,2328828971,2227573024,2935566865,2700099354,3001755655,3168937228,3868552805,3902563182,4203181171,4102977912,3736164937,3501741890,3265478751,3433712980,1106041591,1340463100,1576976609,1408749034,2043211483,2009195472,1708848333,1809054150,832877231,1068351396,766945465,599762354,159417987,126454664,361929877,463180190,2709260871,2943682380,3178106961,3009879386,2572697195,2538681184,2236228733,2336434550,3509871135,3745345300,3441850377,3274667266,3910161971,3877198648,4110568485,4211818798,2597806476,2497604743,2261089178,2295101073,2733856160,2902087851,3202437046,2968011453,3936291284,3835036895,4136440770,4169408201,3535486456,3702665459,3467192302,3231722213,2051518780,1951317047,1716890410,1750902305,1113818384,1282050075,1584504582,1350078989,168810852,67556463,371049330,404016761,841739592,1008918595,775550814,540080725,3969562369,3801332234,4035489047,4269907996,3569255213,3669462566,3366754619,3332740144,2631065433,2463879762,2160117071,2395588676,2767645557,2868897406,3102011747,3069049960,202008497,33778362,270040487,504459436,875451293,975658646,675039627,641025152,2084704233,1917518562,1615861247,1851332852,1147550661,1248802510,1484005843,1451044056,933301370,967311729,733156972,632953703,260388950,25965917,328671808,496906059,1206477858,1239443753,1543208500,1441952575,2144161806,1908694277,1675577880,1842759443,3610369226,3644379585,3408119516,3307916247,4011190502,3776767469,4077384432,4245618683,2809771154,2842737049,3144396420,3043140495,2673705150,2438237621,2203032232,2370213795]);this._mixCol=new Uint8Array(256);for(let e=0;e<256;e++)this._mixCol[e]=e<128?e<<1:e<<1^27;this.buffer=new Uint8Array(16);this.bufferPosition=0}_expandKey(e){unreachable("Cannot call `_expandKey` on the base class")}_decrypt(e,t){let i,a,s;const r=new Uint8Array(16);r.set(e);for(let e=0,i=this._keySize;e<16;++e,++i)r[e]^=t[i];for(let e=this._cyclesOfRepetition-1;e>=1;--e){i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e)r[e]=this._inv_s[r[e]];for(let i=0,a=16*e;i<16;++i,++a)r[i]^=t[a];for(let e=0;e<16;e+=4){const t=this._mix[r[e]],a=this._mix[r[e+1]],s=this._mix[r[e+2]],n=this._mix[r[e+3]];i=t^a>>>8^a<<24^s>>>16^s<<16^n>>>24^n<<8;r[e]=i>>>24&255;r[e+1]=i>>16&255;r[e+2]=i>>8&255;r[e+3]=255&i}}i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e){r[e]=this._inv_s[r[e]];r[e]^=t[e]}return r}_encrypt(e,t){const i=this._s;let a,s,r;const n=new Uint8Array(16);n.set(e);for(let e=0;e<16;++e)n[e]^=t[e];for(let e=1;e<this._cyclesOfRepetition;e++){for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0;e<16;e+=4){const t=n[e+0],i=n[e+1],s=n[e+2],r=n[e+3];a=t^i^s^r;n[e+0]^=a^this._mixCol[t^i];n[e+1]^=a^this._mixCol[i^s];n[e+2]^=a^this._mixCol[s^r];n[e+3]^=a^this._mixCol[r^t]}for(let i=0,a=16*e;i<16;++i,++a)n[i]^=t[a]}for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0,i=this._keySize;e<16;++e,++i)n[e]^=t[i];return n}_decryptBlock2(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];let n=this.iv;for(let t=0;t<i;++t){a[s]=e[t];++s;if(s<16)continue;const i=this._decrypt(a,this._key);for(let e=0;e<16;++e)i[e]^=n[e];n=a;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=n;if(0===r.length)return new Uint8Array(0);let g=16*r.length;if(t){const e=r.at(-1);let t=e[15];if(t<=16){for(let i=15,a=16-t;i>=a;--i)if(e[i]!==t){t=0;break}g-=t;r[r.length-1]=e.subarray(0,16-t)}}const o=new Uint8Array(g);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)o.set(r[e],t);return o}decryptBlock(e,t,i=null){const a=e.length,s=this.buffer;let r=this.bufferPosition;if(i)this.iv=i;else{for(let t=0;r<16&&t<a;++t,++r)s[r]=e[t];if(r<16){this.bufferLength=r;return new Uint8Array(0)}this.iv=s;e=e.subarray(16)}this.buffer=new Uint8Array(16);this.bufferLength=0;this.decryptBlock=this._decryptBlock2;return this.decryptBlock(e,t)}encrypt(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];t||(t=new Uint8Array(16));for(let n=0;n<i;++n){a[s]=e[n];++s;if(s<16)continue;for(let e=0;e<16;++e)a[e]^=t[e];const i=this._encrypt(a,this._key);t=i;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=t;if(0===r.length)return new Uint8Array(0);const n=16*r.length,g=new Uint8Array(n);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)g.set(r[e],t);return g}}class AES128Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=10;this._keySize=160;this._rcon=new Uint8Array([141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141]);this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=this._rcon,a=new Uint8Array(176);a.set(e);for(let e=16,s=1;e<176;++s){let r=a[e-3],n=a[e-2],g=a[e-1],o=a[e-4];r=t[r];n=t[n];g=t[g];o=t[o];r^=i[s];for(let t=0;t<4;++t){a[e]=r^=a[e-16];e++;a[e]=n^=a[e-16];e++;a[e]=g^=a[e-16];e++;a[e]=o^=a[e-16];e++}}return a}}class AES256Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=14;this._keySize=224;this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=new Uint8Array(240);i.set(e);let a,s,r,n,g=1;for(let e=32,o=1;e<240;++o){if(e%32==16){a=t[a];s=t[s];r=t[r];n=t[n]}else if(e%32==0){a=i[e-3];s=i[e-2];r=i[e-1];n=i[e-4];a=t[a];s=t[s];r=t[r];n=t[n];a^=g;(g<<=1)>=256&&(g=255&(27^g))}for(let t=0;t<4;++t){i[e]=a^=i[e-32];e++;i[e]=s^=i[e-32];e++;i[e]=r^=i[e-32];e++;i[e]=n^=i[e-32];e++}}return i}}class PDF17{checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(Ts(s,0,s.length),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(Ts(a,0,a.length),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=Ts(s,0,s.length);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=Ts(a,0,a.length);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class PDF20{_hash(e,t,i){let a=Ts(t,0,t.length).subarray(0,32),s=[0],r=0;for(;r<64||s.at(-1)>r-32;){const t=e.length+a.length+i.length,c=new Uint8Array(t);let C=0;c.set(e,C);C+=e.length;c.set(a,C);C+=a.length;c.set(i,C);const h=new Uint8Array(64*t);for(let e=0,i=0;e<64;e++,i+=t)h.set(c,i);s=new AES128Cipher(a.subarray(0,16)).encrypt(h,a.subarray(16,32));const l=s.slice(0,16).reduce(((e,t)=>e+t),0)%3;0===l?a=Ts(s,0,s.length):1===l?a=(n=s,g=0,o=s.length,qs(n,g,o,!0)):2===l&&(a=qs(s,0,s.length));r++}var n,g,o;return a.subarray(0,32)}checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(this._hash(e,s,i),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(this._hash(e,a,[]),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=this._hash(e,s,i);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=this._hash(e,a,[]);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class CipherTransform{constructor(e,t){this.StringCipherConstructor=e;this.StreamCipherConstructor=t}createStream(e,t){const i=new this.StreamCipherConstructor;return new DecryptStream(e,t,(function cipherTransformDecryptStream(e,t){return i.decryptBlock(e,t)}))}decryptString(e){const t=new this.StringCipherConstructor;let i=stringToBytes(e);i=t.decryptBlock(i,!0);return bytesToString(i)}encryptString(e){const t=new this.StringCipherConstructor;if(t instanceof AESBaseCipher){const i=16-e.length%16;e+=String.fromCharCode(i).repeat(i);const a=new Uint8Array(16);if("undefined"!=typeof crypto)crypto.getRandomValues(a);else for(let e=0;e<16;e++)a[e]=Math.floor(256*Math.random());let s=stringToBytes(e);s=t.encrypt(s,a);const r=new Uint8Array(16+s.length);r.set(a);r.set(s,16);return bytesToString(r)}let i=stringToBytes(e);i=t.encrypt(i);return bytesToString(i)}}class CipherTransformFactory{static#k=new Uint8Array([40,191,78,94,78,117,138,65,100,0,78,86,255,250,1,8,46,46,0,182,208,104,62,128,47,12,169,254,100,83,105,122]);#R(e,t,i,a,s,r,n,g,o,c,C,h){if(t){const e=Math.min(127,t.length);t=t.subarray(0,e)}else t=[];const l=6===e?new PDF20:new PDF17;return l.checkUserPassword(t,g,n)?l.getUserKey(t,o,C):t.length&&l.checkOwnerPassword(t,a,r,i)?l.getOwnerKey(t,s,r,c):null}#N(e,t,i,a,s,r,n,g){const o=40+i.length+e.length,c=new Uint8Array(o);let C,h,l=0;if(t){h=Math.min(32,t.length);for(;l<h;++l)c[l]=t[l]}C=0;for(;l<32;)c[l++]=CipherTransformFactory.#k[C++];for(C=0,h=i.length;C<h;++C)c[l++]=i[C];c[l++]=255&s;c[l++]=s>>8&255;c[l++]=s>>16&255;c[l++]=s>>>24&255;for(C=0,h=e.length;C<h;++C)c[l++]=e[C];if(r>=4&&!g){c[l++]=255;c[l++]=255;c[l++]=255;c[l++]=255}let Q=Ks(c,0,l);const E=n>>3;if(r>=3)for(C=0;C<50;++C)Q=Ks(Q,0,E);const u=Q.subarray(0,E);let d,f;if(r>=3){for(l=0;l<32;++l)c[l]=CipherTransformFactory.#k[l];for(C=0,h=e.length;C<h;++C)c[l++]=e[C];d=new ARCFourCipher(u);f=d.encryptBlock(Ks(c,0,l));h=u.length;const t=new Uint8Array(h);for(C=1;C<=19;++C){for(let e=0;e<h;++e)t[e]=u[e]^C;d=new ARCFourCipher(t);f=d.encryptBlock(f)}for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}else{d=new ARCFourCipher(u);f=d.encryptBlock(CipherTransformFactory.#k);for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}return u}#G(e,t,i,a){const s=new Uint8Array(32);let r=0;const n=Math.min(32,e.length);for(;r<n;++r)s[r]=e[r];let g=0;for(;r<32;)s[r++]=CipherTransformFactory.#k[g++];let o=Ks(s,0,r);const c=a>>3;if(i>=3)for(g=0;g<50;++g)o=Ks(o,0,o.length);let C,h;if(i>=3){h=t;const e=new Uint8Array(c);for(g=19;g>=0;g--){for(let t=0;t<c;++t)e[t]=o[t]^g;C=new ARCFourCipher(e);h=C.encryptBlock(h)}}else{C=new ARCFourCipher(o.subarray(0,c));h=C.encryptBlock(t)}return h}#x(e,t,i,a=!1){const s=new Uint8Array(i.length+9),r=i.length;let n;for(n=0;n<r;++n)s[n]=i[n];s[n++]=255&e;s[n++]=e>>8&255;s[n++]=e>>16&255;s[n++]=255&t;s[n++]=t>>8&255;if(a){s[n++]=115;s[n++]=65;s[n++]=108;s[n++]=84}return Ks(s,0,n).subarray(0,Math.min(i.length+5,16))}#U(e,t,i,a,s){if(!(t instanceof Name))throw new FormatError("Invalid crypt filter name.");const r=this,n=e.get(t.name),g=n?.get("CFM");if(!g||"None"===g.name)return function(){return new NullCipher};if("V2"===g.name)return function(){return new ARCFourCipher(r.#x(i,a,s,!1))};if("AESV2"===g.name)return function(){return new AES128Cipher(r.#x(i,a,s,!0))};if("AESV3"===g.name)return function(){return new AES256Cipher(s)};throw new FormatError("Unknown crypto method")}constructor(e,t,i){const a=e.get("Filter");if(!isName(a,"Standard"))throw new FormatError("unknown encryption method");this.filterName=a.name;this.dict=e;const s=e.get("V");if(!Number.isInteger(s)||1!==s&&2!==s&&4!==s&&5!==s)throw new FormatError("unsupported encryption algorithm");this.algorithm=s;let r=e.get("Length");if(!r)if(s<=3)r=40;else{const t=e.get("CF"),i=e.get("StmF");if(t instanceof Dict&&i instanceof Name){t.suppressEncryption=!0;const e=t.get(i.name);r=e?.get("Length")||128;r<40&&(r<<=3)}}if(!Number.isInteger(r)||r<40||r%8!=0)throw new FormatError("invalid key length");const n=stringToBytes(e.get("O")),g=stringToBytes(e.get("U")),o=n.subarray(0,32),c=g.subarray(0,32),C=e.get("P"),h=e.get("R"),l=(4===s||5===s)&&!1!==e.get("EncryptMetadata");this.encryptMetadata=l;const Q=stringToBytes(t);let E,u;if(i){if(6===h)try{i=utf8StringToString(i)}catch{warn("CipherTransformFactory: Unable to convert UTF8 encoded password.")}E=stringToBytes(i)}if(5!==s)u=this.#N(Q,E,o,c,C,h,r,l);else{const t=n.subarray(32,40),i=n.subarray(40,48),a=g.subarray(0,48),s=g.subarray(32,40),r=g.subarray(40,48),C=stringToBytes(e.get("OE")),l=stringToBytes(e.get("UE")),Q=stringToBytes(e.get("Perms"));u=this.#R(h,E,o,t,i,a,c,s,r,C,l,Q)}if(!u&&!i)throw new PasswordException("No password given",rt);if(!u&&i){const e=this.#G(E,o,h,r);u=this.#N(Q,e,o,c,C,h,r,l)}if(!u)throw new PasswordException("Incorrect Password",nt);this.encryptionKey=u;if(s>=4){const t=e.get("CF");t instanceof Dict&&(t.suppressEncryption=!0);this.cf=t;this.stmf=e.get("StmF")||Name.get("Identity");this.strf=e.get("StrF")||Name.get("Identity");this.eff=e.get("EFF")||this.stmf}}createCipherTransform(e,t){if(4===this.algorithm||5===this.algorithm)return new CipherTransform(this.#U(this.cf,this.strf,e,t,this.encryptionKey),this.#U(this.cf,this.stmf,e,t,this.encryptionKey));const i=this.#x(e,t,this.encryptionKey,!1),cipherConstructor=function(){return new ARCFourCipher(i)};return new CipherTransform(cipherConstructor,cipherConstructor)}}async function writeObject(e,t,i,{encrypt:a=null}){const s=a?.createCipherTransform(e.num,e.gen);i.push(`${e.num} ${e.gen} obj\n`);t instanceof Dict?await writeDict(t,i,s):t instanceof BaseStream?await writeStream(t,i,s):(Array.isArray(t)||ArrayBuffer.isView(t))&&await writeArray(t,i,s);i.push("\nendobj\n")}async function writeDict(e,t,i){t.push("<<");for(const a of e.getKeys()){t.push(` /${escapePDFName(a)} `);await writeValue(e.getRaw(a),t,i)}t.push(">>")}async function writeStream(e,t,i){let a=e.getBytes();const{dict:s}=e,[r,n]=await Promise.all([s.getAsync("Filter"),s.getAsync("DecodeParms")]),g=isName(Array.isArray(r)?await s.xref.fetchIfRefAsync(r[0]):r,"FlateDecode");if(a.length>=256||g)try{const e=new CompressionStream("deflate"),t=e.writable.getWriter();t.write(a);t.close();const i=await new Response(e.readable).arrayBuffer();a=new Uint8Array(i);let o,c;if(r){if(!g){o=Array.isArray(r)?[Name.get("FlateDecode"),...r]:[Name.get("FlateDecode"),r];n&&(c=Array.isArray(n)?[null,...n]:[null,n])}}else o=Name.get("FlateDecode");o&&s.set("Filter",o);c&&s.set("DecodeParms",c)}catch(e){info(`writeStream - cannot compress data: "${e}".`)}let o=bytesToString(a);i&&(o=i.encryptString(o));s.set("Length",o.length);await writeDict(s,t,i);t.push(" stream\n",o,"\nendstream")}async function writeArray(e,t,i){t.push("[");let a=!0;for(const s of e){a?a=!1:t.push(" ");await writeValue(s,t,i)}t.push("]")}async function writeValue(e,t,i){if(e instanceof Name)t.push(`/${escapePDFName(e.name)}`);else if(e instanceof Ref)t.push(`${e.num} ${e.gen} R`);else if(Array.isArray(e)||ArrayBuffer.isView(e))await writeArray(e,t,i);else if("string"==typeof e){i&&(e=i.encryptString(e));t.push(`(${escapeString(e)})`)}else"number"==typeof e?t.push(numberToString(e)):"boolean"==typeof e?t.push(e.toString()):e instanceof Dict?await writeDict(e,t,i):e instanceof BaseStream?await writeStream(e,t,i):null===e?t.push("null"):warn(`Unhandled value in writer: ${typeof e}, please file a bug.`)}function writeInt(e,t,i,a){for(let s=t+i-1;s>i-1;s--){a[s]=255&e;e>>=8}return i+t}function writeString(e,t,i){for(let a=0,s=e.length;a<s;a++)i[t+a]=255&e.charCodeAt(a)}function updateXFA({xfaData:e,xfaDatasetsRef:t,newRefs:i,xref:a}){if(null===e){e=function writeXFADataForAcroform(e,t){const i=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e);for(const{xfa:e}of t){if(!e)continue;const{path:t,value:a}=e;if(!t)continue;const s=parseXFAPath(t);let r=i.documentElement.searchNode(s,0);!r&&s.length>1&&(r=i.documentElement.searchNode([s.at(-1)],0));r?r.childNodes=Array.isArray(a)?a.map((e=>new SimpleDOMNode("value",e))):[new SimpleDOMNode("#text",a)]:warn(`Node not found for path: ${t}`)}const a=[];i.documentElement.dump(a);return a.join("")}(a.fetchIfRef(t).getString(),i)}const s=a.encrypt;if(s){e=s.createCipherTransform(t.num,t.gen).encryptString(e)}const r=`${t.num} ${t.gen} obj\n<< /Type /EmbeddedFile /Length ${e.length}>>\nstream\n`+e+"\nendstream\nendobj\n";i.push({ref:t,data:r})}function getIndexes(e){const t=[];for(const{ref:i}of e)i.num===t.at(-2)+t.at(-1)?t[t.length-1]+=1:t.push(i.num,1);return t}function computeIDs(e,t,i){if(Array.isArray(t.fileIds)&&t.fileIds.length>0){const a=function computeMD5(e,t){const i=Math.floor(Date.now()/1e3),a=t.filename||"",s=[i.toString(),a,e.toString()];let r=s.reduce(((e,t)=>e+t.length),0);for(const e of Object.values(t.info)){s.push(e);r+=e.length}const n=new Uint8Array(r);let g=0;for(const e of s){writeString(e,g,n);g+=e.length}return bytesToString(Ks(n))}(e,t);i.set("ID",[t.fileIds[0],a])}}async function incrementalUpdate({originalData:e,xrefInfo:t,newRefs:i,xref:a=null,hasXfa:s=!1,xfaDatasetsRef:r=null,hasXfaDatasetsEntry:n=!1,needAppearances:g,acroFormRef:o=null,acroForm:c=null,xfaData:C=null,useXrefStream:h=!1}){await async function updateAcroform({xref:e,acroForm:t,acroFormRef:i,hasXfa:a,hasXfaDatasetsEntry:s,xfaDatasetsRef:r,needAppearances:n,newRefs:g}){!a||s||r||warn("XFA - Cannot save it");if(!n&&(!a||!r||s))return;const o=t.clone();if(a&&!s){const e=t.get("XFA").slice();e.splice(2,0,"datasets");e.splice(3,0,r);o.set("XFA",e)}n&&o.set("NeedAppearances",!0);const c=[];await writeObject(i,o,c,e);g.push({ref:i,data:c.join("")})}({xref:a,acroForm:c,acroFormRef:o,hasXfa:s,hasXfaDatasetsEntry:n,xfaDatasetsRef:r,needAppearances:g,newRefs:i});s&&updateXFA({xfaData:C,xfaDatasetsRef:r,newRefs:i,xref:a});const l=[];let Q=e.length;const E=e.at(-1);if(10!==E&&13!==E){l.push("\n");Q+=1}const u=function getTrailerDict(e,t,i){const a=new Dict(null);a.set("Prev",e.startXRef);const s=e.newRef;if(i){t.push({ref:s,data:""});a.set("Size",s.num+1);a.set("Type",Name.get("XRef"))}else a.set("Size",s.num);null!==e.rootRef&&a.set("Root",e.rootRef);null!==e.infoRef&&a.set("Info",e.infoRef);null!==e.encryptRef&&a.set("Encrypt",e.encryptRef);return a}(t,i,h);i=i.sort(((e,t)=>e.ref.num-t.ref.num));for(const{data:e}of i)null!==e&&l.push(e);await(h?async function getXRefStreamTable(e,t,i,a,s){const r=[];let n=0,g=0;for(const{ref:e,data:a}of i){let i;n=Math.max(n,t);if(null!==a){i=Math.min(e.gen,65535);r.push([1,t,i]);t+=a.length}else{i=Math.min(e.gen+1,65535);r.push([0,0,i])}g=Math.max(g,i)}a.set("Index",getIndexes(i));const o=[1,getSizeInBytes(n),getSizeInBytes(g)];a.set("W",o);computeIDs(t,e,a);const c=o.reduce(((e,t)=>e+t),0),C=new Uint8Array(c*r.length),h=new Stream(C);h.dict=a;let l=0;for(const[e,t,i]of r){l=writeInt(e,o[0],l,C);l=writeInt(t,o[1],l,C);l=writeInt(i,o[2],l,C)}await writeObject(e.newRef,h,s,{});s.push("startxref\n",t.toString(),"\n%%EOF\n")}(t,Q,i,u,l):async function getXRefTable(e,t,i,a,s){s.push("xref\n");const r=getIndexes(i);let n=0;for(const{ref:e,data:a}of i){if(e.num===r[n]){s.push(`${r[n]} ${r[n+1]}\n`);n+=2}if(null!==a){s.push(`${t.toString().padStart(10,"0")} ${Math.min(e.gen,65535).toString().padStart(5,"0")} n\r\n`);t+=a.length}else s.push(`0000000000 ${Math.min(e.gen+1,65535).toString().padStart(5,"0")} f\r\n`)}computeIDs(t,e,a);s.push("trailer\n");await writeDict(a,s);s.push("\nstartxref\n",t.toString(),"\n%%EOF\n")}(t,Q,i,u,l));const d=l.reduce(((e,t)=>e+t.length),e.length),f=new Uint8Array(d);f.set(e);let p=e.length;for(const e of l){writeString(e,p,f);p+=e.length}return f}const Os=1,Ws=2,js=3,Xs=4,Zs=5;class StructTreeRoot{constructor(e,t){this.dict=e;this.ref=t instanceof Ref?t:null;this.roleMap=new Map;this.structParentIds=null}init(){this.readRoleMap()}#M(e,t,i){if(!(e instanceof Ref)||t<0)return;this.structParentIds||=new RefSetCache;let a=this.structParentIds.get(e);if(!a){a=[];this.structParentIds.put(e,a)}a.push([t,i])}addAnnotationIdToPage(e,t){this.#M(e,t,Xs)}readRoleMap(){const e=this.dict.get("RoleMap");e instanceof Dict&&e.forEach(((e,t)=>{t instanceof Name&&this.roleMap.set(e,t.name)}))}static async canCreateStructureTree({catalogRef:e,pdfManager:t,newAnnotationsByPage:i}){if(!(e instanceof Ref)){warn("Cannot save the struct tree: no catalog reference.");return!1}let a=0,s=!0;for(const[e,r]of i){const{ref:i}=await t.getPage(e);if(!(i instanceof Ref)){warn(`Cannot save the struct tree: page ${e} has no ref.`);s=!0;break}for(const e of r)if(e.accessibilityData?.type){e.parentTreeId=a++;s=!1}}if(s){for(const e of i.values())for(const t of e)delete t.parentTreeId;return!1}return!0}static async createStructureTree({newAnnotationsByPage:e,xref:t,catalogRef:i,pdfManager:a,newRefs:s}){const r=a.catalog.cloneDict(),n=new RefSetCache;n.put(i,r);const g=t.getNewTemporaryRef();r.set("StructTreeRoot",g);const o=new Dict(t);o.set("Type",Name.get("StructTreeRoot"));const c=t.getNewTemporaryRef();o.set("ParentTree",c);const C=[];o.set("K",C);n.put(g,o);const h=new Dict(t),l=[];h.set("Nums",l);const Q=await this.#L({newAnnotationsByPage:e,structTreeRootRef:g,structTreeRoot:null,kids:C,nums:l,xref:t,pdfManager:a,newRefs:s,cache:n});o.set("ParentTreeNextKey",Q);n.put(c,h);const E=[];for(const[e,i]of n.items()){E.length=0;await writeObject(e,i,E,t);s.push({ref:e,data:E.join("")})}}async canUpdateStructTree({pdfManager:e,xref:t,newAnnotationsByPage:i}){if(!this.ref){warn("Cannot update the struct tree: no root reference.");return!1}let a=this.dict.get("ParentTreeNextKey");if(!Number.isInteger(a)||a<0){warn("Cannot update the struct tree: invalid next key.");return!1}const s=this.dict.get("ParentTree");if(!(s instanceof Dict)){warn("Cannot update the struct tree: ParentTree isn't a dict.");return!1}const r=s.get("Nums");if(!Array.isArray(r)){warn("Cannot update the struct tree: nums isn't an array.");return!1}const n=new NumberTree(s,t);for(const t of i.keys()){const{pageDict:i}=await e.getPage(t);if(!i.has("StructParents"))continue;const a=i.get("StructParents");if(!Number.isInteger(a)||!Array.isArray(n.get(a))){warn(`Cannot save the struct tree: page ${t} has a wrong id.`);return!1}}let g=!0;for(const[t,s]of i){const{pageDict:i}=await e.getPage(t);StructTreeRoot.#H({elements:s,xref:this.dict.xref,pageDict:i,numberTree:n});for(const e of s)if(e.accessibilityData?.type){e.accessibilityData.structParent>=0||(e.parentTreeId=a++);g=!1}}if(g){for(const e of i.values())for(const t of e){delete t.parentTreeId;delete t.structTreeParent}return!1}return!0}async updateStructureTree({newAnnotationsByPage:e,pdfManager:t,newRefs:i}){const a=this.dict.xref,s=this.dict.clone(),r=this.ref,n=new RefSetCache;n.put(r,s);let g,o=s.getRaw("ParentTree");if(o instanceof Ref)g=a.fetch(o);else{g=o;o=a.getNewTemporaryRef();s.set("ParentTree",o)}g=g.clone();n.put(o,g);let c=g.getRaw("Nums"),C=null;if(c instanceof Ref){C=c;c=a.fetch(C)}c=c.slice();C||g.set("Nums",c);const h=await StructTreeRoot.#L({newAnnotationsByPage:e,structTreeRootRef:r,structTreeRoot:this,kids:null,nums:c,xref:a,pdfManager:t,newRefs:i,cache:n});if(-1===h)return;s.set("ParentTreeNextKey",h);C&&n.put(C,c);const l=[];for(const[e,t]of n.items()){l.length=0;await writeObject(e,t,l,a);i.push({ref:e,data:l.join("")})}}static async#L({newAnnotationsByPage:e,structTreeRootRef:t,structTreeRoot:i,kids:a,nums:s,xref:r,pdfManager:n,newRefs:g,cache:o}){const c=Name.get("OBJR");let C,h=-1;const l=[];for(const[Q,E]of e){const e=await n.getPage(Q),{ref:u}=e,d=u instanceof Ref;for(const{accessibilityData:n,ref:f,parentTreeId:p,structTreeParent:m}of E){if(!n?.type)continue;const{structParent:E}=n;if(i&&Number.isInteger(E)&&E>=0){let t=(C||=new Map).get(Q);if(void 0===t){t=new StructTreePage(i,e.pageDict).collectObjects(u);C.set(Q,t)}const a=t?.get(E);if(a){const e=r.fetch(a).clone();StructTreeRoot.#J(e,n);l.length=0;await writeObject(a,e,l,r);g.push({ref:a,data:l.join("")});continue}}h=Math.max(h,p);const y=r.getNewTemporaryRef(),w=new Dict(r);StructTreeRoot.#J(w,n);await this.#Y({structTreeParent:m,tagDict:w,newTagRef:y,structTreeRootRef:t,fallbackKids:a,xref:r,cache:o});const D=new Dict(r);w.set("K",D);D.set("Type",c);d&&D.set("Pg",u);D.set("Obj",f);o.put(y,w);s.push(p,y)}}return h+1}static#J(e,{type:t,title:i,lang:a,alt:s,expanded:r,actualText:n}){e.set("S",Name.get(t));i&&e.set("T",stringToAsciiOrUTF16BE(i));a&&e.set("Lang",stringToAsciiOrUTF16BE(a));s&&e.set("Alt",stringToAsciiOrUTF16BE(s));r&&e.set("E",stringToAsciiOrUTF16BE(r));n&&e.set("ActualText",stringToAsciiOrUTF16BE(n))}static#H({elements:e,xref:t,pageDict:i,numberTree:a}){const s=new Map;for(const t of e)if(t.structTreeParentId){const e=parseInt(t.structTreeParentId.split("_mc")[1],10);let i=s.get(e);if(!i){i=[];s.set(e,i)}i.push(t)}const r=i.get("StructParents");if(!Number.isInteger(r))return;const n=a.get(r),updateElement=(e,i,a)=>{const r=s.get(e);if(r){const e=i.getRaw("P"),s=t.fetchIfRef(e);if(e instanceof Ref&&s instanceof Dict){const e={ref:a,dict:i};for(const t of r)t.structTreeParent=e}return!0}return!1};for(const e of n){if(!(e instanceof Ref))continue;const i=t.fetch(e),a=i.get("K");if(Number.isInteger(a))updateElement(a,i,e);else if(Array.isArray(a))for(let s of a){s=t.fetchIfRef(s);if(Number.isInteger(s)&&updateElement(s,i,e))break;if(!(s instanceof Dict))continue;if(!isName(s.get("Type"),"MCR"))break;const a=s.get("MCID");if(Number.isInteger(a)&&updateElement(a,i,e))break}}}static async#Y({structTreeParent:e,tagDict:t,newTagRef:i,structTreeRootRef:a,fallbackKids:s,xref:r,cache:n}){let g,o=null;if(e){({ref:o}=e);g=e.dict.getRaw("P")||a}else g=a;t.set("P",g);const c=r.fetchIfRef(g);if(!c){s.push(i);return}let C=n.get(g);if(!C){C=c.clone();n.put(g,C)}const h=C.getRaw("K");let l=h instanceof Ref?n.get(h):null;if(!l){l=r.fetchIfRef(h);l=Array.isArray(l)?l.slice():[h];const e=r.getNewTemporaryRef();C.set("K",e);n.put(e,l)}const Q=l.indexOf(o);l.splice(Q>=0?Q+1:l.length,0,i)}}class StructElementNode{constructor(e,t){this.tree=e;this.dict=t;this.kids=[];this.parseKids()}get role(){const e=this.dict.get("S"),t=e instanceof Name?e.name:"",{root:i}=this.tree;return i.roleMap.has(t)?i.roleMap.get(t):t}parseKids(){let e=null;const t=this.dict.getRaw("Pg");t instanceof Ref&&(e=t.toString());const i=this.dict.get("K");if(Array.isArray(i))for(const t of i){const i=this.parseKid(e,t);i&&this.kids.push(i)}else{const t=this.parseKid(e,i);t&&this.kids.push(t)}}parseKid(e,t){if(Number.isInteger(t))return this.tree.pageDict.objId!==e?null:new StructElement({type:Os,mcid:t,pageObjId:e});let i=null;t instanceof Ref?i=this.dict.xref.fetch(t):t instanceof Dict&&(i=t);if(!i)return null;const a=i.getRaw("Pg");a instanceof Ref&&(e=a.toString());const s=i.get("Type")instanceof Name?i.get("Type").name:null;if("MCR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Stm");return new StructElement({type:Ws,refObjId:t instanceof Ref?t.toString():null,pageObjId:e,mcid:i.get("MCID")})}if("OBJR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Obj");return new StructElement({type:js,refObjId:t instanceof Ref?t.toString():null,pageObjId:e})}return new StructElement({type:Zs,dict:i})}}class StructElement{constructor({type:e,dict:t=null,mcid:i=null,pageObjId:a=null,refObjId:s=null}){this.type=e;this.dict=t;this.mcid=i;this.pageObjId=a;this.refObjId=s;this.parentNode=null}}class StructTreePage{constructor(e,t){this.root=e;this.rootDict=e?e.dict:null;this.pageDict=t;this.nodes=[]}collectObjects(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return null;const t=this.rootDict.get("ParentTree");if(!t)return null;const i=this.root.structParentIds?.get(e);if(!i)return null;const a=new Map,s=new NumberTree(t,this.rootDict.xref);for(const[e]of i){const t=s.getRaw(e);t instanceof Ref&&a.set(e,t)}return a}parse(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return;const t=this.rootDict.get("ParentTree");if(!t)return;const i=this.pageDict.get("StructParents"),a=this.root.structParentIds?.get(e);if(!Number.isInteger(i)&&!a)return;const s=new Map,r=new NumberTree(t,this.rootDict.xref);if(Number.isInteger(i)){const e=r.get(i);if(Array.isArray(e))for(const t of e)t instanceof Ref&&this.addNode(this.rootDict.xref.fetch(t),s)}if(a)for(const[e,t]of a){const i=r.get(e);if(i){const e=this.addNode(this.rootDict.xref.fetchIfRef(i),s);1===e?.kids?.length&&e.kids[0].type===js&&(e.kids[0].type=t)}}}addNode(e,t,i=0){if(i>40){warn("StructTree MAX_DEPTH reached.");return null}if(!(e instanceof Dict))return null;if(t.has(e))return t.get(e);const a=new StructElementNode(this,e);t.set(e,a);const s=e.get("P");if(!s||isName(s.get("Type"),"StructTreeRoot")){this.addTopLevelNode(e,a)||t.delete(e);return a}const r=this.addNode(s,t,i+1);if(!r)return a;let n=!1;for(const t of r.kids)if(t.type===Zs&&t.dict===e){t.parentNode=a;n=!0}n||t.delete(e);return a}addTopLevelNode(e,t){const i=this.rootDict.get("K");if(!i)return!1;if(i instanceof Dict){if(i.objId!==e.objId)return!1;this.nodes[0]=t;return!0}if(!Array.isArray(i))return!0;let a=!1;for(let s=0;s<i.length;s++){const r=i[s];if(r?.toString()===e.objId){this.nodes[s]=t;a=!0}}return a}get serializable(){function nodeToSerializable(e,t,i=0){if(i>40){warn("StructTree too deep to be fully serialized.");return}const a=Object.create(null);a.role=e.role;a.children=[];t.children.push(a);let s=e.dict.get("Alt");"string"!=typeof s&&(s=e.dict.get("ActualText"));"string"==typeof s&&(a.alt=stringToPDFString(s));const r=e.dict.get("A");if(r instanceof Dict){const e=lookupNormalRect(r.getArray("BBox"),null);if(e)a.bbox=e;else{const e=r.get("Width"),t=r.get("Height");"number"==typeof e&&e>0&&"number"==typeof t&&t>0&&(a.bbox=[0,0,e,t])}}const n=e.dict.get("Lang");"string"==typeof n&&(a.lang=stringToPDFString(n));for(const t of e.kids){const e=t.type===Zs?t.parentNode:null;e?nodeToSerializable(e,a,i+1):t.type===Os||t.type===Ws?a.children.push({type:"content",id:`p${t.pageObjId}_mc${t.mcid}`}):t.type===js?a.children.push({type:"object",id:t.refObjId}):t.type===Xs&&a.children.push({type:"annotation",id:`pdfjs_internal_id_${t.refObjId}`})}}const e=Object.create(null);e.children=[];e.role="Root";for(const t of this.nodes)t&&nodeToSerializable(t,e);return e}}function isValidExplicitDest(e){if(!Array.isArray(e)||e.length<2)return!1;const[t,i,...a]=e;if(!(t instanceof Ref||Number.isInteger(t)))return!1;if(!(i instanceof Name))return!1;const s=a.length;let r=!0;switch(i.name){case"XYZ":if(s<2||s>3)return!1;break;case"Fit":case"FitB":return 0===s;case"FitH":case"FitBH":case"FitV":case"FitBV":if(s>1)return!1;break;case"FitR":if(4!==s)return!1;r=!1;break;default:return!1}for(const e of a)if(!("number"==typeof e||r&&null===e))return!1;return!0}function fetchDest(e){e instanceof Dict&&(e=e.get("D"));return isValidExplicitDest(e)?e:null}function fetchRemoteDest(e){let t=e.get("D");if(t){t instanceof Name&&(t=t.name);if("string"==typeof t)return stringToPDFString(t);if(isValidExplicitDest(t))return JSON.stringify(t)}return null}class Catalog{constructor(e,t){this.pdfManager=e;this.xref=t;this._catDict=t.getCatalogObj();if(!(this._catDict instanceof Dict))throw new FormatError("Catalog object is not a dictionary.");this.toplevelPagesDict;this._actualNumPages=null;this.fontCache=new RefSetCache;this.builtInCMapCache=new Map;this.standardFontDataCache=new Map;this.globalImageCache=new GlobalImageCache;this.pageKidsCountCache=new RefSetCache;this.pageIndexCache=new RefSetCache;this.pageDictCache=new RefSetCache;this.nonBlendModesSet=new RefSet;this.systemFontCache=new Map}cloneDict(){return this._catDict.clone()}get version(){const e=this._catDict.get("Version");if(e instanceof Name){if(kt.test(e.name))return shadow(this,"version",e.name);warn(`Invalid PDF catalog version: ${e.name}`)}return shadow(this,"version",null)}get lang(){const e=this._catDict.get("Lang");return shadow(this,"lang",e&&"string"==typeof e?stringToPDFString(e):null)}get needsRendering(){const e=this._catDict.get("NeedsRendering");return shadow(this,"needsRendering","boolean"==typeof e&&e)}get collection(){let e=null;try{const t=this._catDict.get("Collection");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch Collection entry; assuming no collection is present.")}return shadow(this,"collection",e)}get acroForm(){let e=null;try{const t=this._catDict.get("AcroForm");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch AcroForm entry; assuming no forms are present.")}return shadow(this,"acroForm",e)}get acroFormRef(){const e=this._catDict.getRaw("AcroForm");return shadow(this,"acroFormRef",e instanceof Ref?e:null)}get metadata(){const e=this._catDict.getRaw("Metadata");if(!(e instanceof Ref))return shadow(this,"metadata",null);let t=null;try{const i=this.xref.fetch(e,!this.xref.encrypt?.encryptMetadata);if(i instanceof BaseStream&&i.dict instanceof Dict){const e=i.dict.get("Type"),a=i.dict.get("Subtype");if(isName(e,"Metadata")&&isName(a,"XML")){const e=stringToUTF8String(i.getString());e&&(t=new MetadataParser(e).serializable)}}}catch(e){if(e instanceof MissingDataException)throw e;info(`Skipping invalid Metadata: "${e}".`)}return shadow(this,"metadata",t)}get markInfo(){let e=null;try{e=this._readMarkInfo()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read mark info.")}return shadow(this,"markInfo",e)}_readMarkInfo(){const e=this._catDict.get("MarkInfo");if(!(e instanceof Dict))return null;const t={Marked:!1,UserProperties:!1,Suspects:!1};for(const i in t){const a=e.get(i);"boolean"==typeof a&&(t[i]=a)}return t}get structTreeRoot(){let e=null;try{e=this._readStructTreeRoot()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable read to structTreeRoot info.")}return shadow(this,"structTreeRoot",e)}_readStructTreeRoot(){const e=this._catDict.getRaw("StructTreeRoot"),t=this.xref.fetchIfRef(e);if(!(t instanceof Dict))return null;const i=new StructTreeRoot(t,e);i.init();return i}get toplevelPagesDict(){const e=this._catDict.get("Pages");if(!(e instanceof Dict))throw new FormatError("Invalid top-level pages dictionary.");return shadow(this,"toplevelPagesDict",e)}get documentOutline(){let e=null;try{e=this._readDocumentOutline()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutline",e)}_readDocumentOutline(){let e=this._catDict.get("Outlines");if(!(e instanceof Dict))return null;e=e.getRaw("First");if(!(e instanceof Ref))return null;const t={items:[]},i=[{obj:e,parent:t}],a=new RefSet;a.put(e);const s=this.xref,r=new Uint8ClampedArray(3);for(;i.length>0;){const t=i.shift(),n=s.fetchIfRef(t.obj);if(null===n)continue;n.has("Title")||warn("Invalid outline item encountered.");const g={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:n,resultObj:g,docBaseUrl:this.baseUrl,docAttachments:this.attachments});const o=n.get("Title"),c=n.get("F")||0,C=n.getArray("C"),h=n.get("Count");let l=r;!isNumberArray(C,3)||0===C[0]&&0===C[1]&&0===C[2]||(l=ColorSpace.singletons.rgb.getRgb(C,0));const Q={action:g.action,attachment:g.attachment,dest:g.dest,url:g.url,unsafeUrl:g.unsafeUrl,newWindow:g.newWindow,setOCGState:g.setOCGState,title:"string"==typeof o?stringToPDFString(o):"",color:l,count:Number.isInteger(h)?h:void 0,bold:!!(2&c),italic:!!(1&c),items:[]};t.parent.items.push(Q);e=n.getRaw("First");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:Q});a.put(e)}e=n.getRaw("Next");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:t.parent});a.put(e)}}return t.items.length>0?t.items:null}get permissions(){let e=null;try{e=this._readPermissions()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read permissions.")}return shadow(this,"permissions",e)}_readPermissions(){const e=this.xref.trailer.get("Encrypt");if(!(e instanceof Dict))return null;let t=e.get("P");if("number"!=typeof t)return null;t+=2**32;const i=[];for(const e in y){const a=y[e];t&a&&i.push(a)}return i}get optionalContentConfig(){let e=null;try{const t=this._catDict.get("OCProperties");if(!t)return shadow(this,"optionalContentConfig",null);const i=t.get("D");if(!i)return shadow(this,"optionalContentConfig",null);const a=t.get("OCGs");if(!Array.isArray(a))return shadow(this,"optionalContentConfig",null);const s=[],r=new RefSet;for(const e of a)if(e instanceof Ref&&!r.has(e)){r.put(e);s.push(this.#v(e))}e=this.#K(i,r);e.groups=s}catch(e){if(e instanceof MissingDataException)throw e;warn(`Unable to read optional content config: ${e}`)}return shadow(this,"optionalContentConfig",e)}#v(e){const t=this.xref.fetch(e),i={id:e.toString(),name:null,intent:null,usage:{print:null,view:null}},a=t.get("Name");"string"==typeof a&&(i.name=stringToPDFString(a));let s=t.getArray("Intent");Array.isArray(s)||(s=[s]);s.every((e=>e instanceof Name))&&(i.intent=s.map((e=>e.name)));const r=t.get("Usage");if(!(r instanceof Dict))return i;const n=i.usage,g=r.get("Print");if(g instanceof Dict){const e=g.get("PrintState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.print={printState:e.name}}}const o=r.get("View");if(o instanceof Dict){const e=o.get("ViewState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.view={viewState:e.name}}}return i}#K(e,t){function parseOnOff(e){const i=[];if(Array.isArray(e))for(const a of e)a instanceof Ref&&t.has(a)&&i.push(a.toString());return i}function parseOrder(e,i=0){if(!Array.isArray(e))return null;const s=[];for(const r of e){if(r instanceof Ref&&t.has(r)){a.put(r);s.push(r.toString());continue}const e=parseNestedOrder(r,i);e&&s.push(e)}if(i>0)return s;const r=[];for(const e of t)a.has(e)||r.push(e.toString());r.length&&s.push({name:null,order:r});return s}function parseNestedOrder(e,t){if(++t>s){warn("parseNestedOrder - reached MAX_NESTED_LEVELS.");return null}const a=i.fetchIfRef(e);if(!Array.isArray(a))return null;const r=i.fetchIfRef(a[0]);if("string"!=typeof r)return null;const n=parseOrder(a.slice(1),t);return n&&n.length?{name:stringToPDFString(r),order:n}:null}const i=this.xref,a=new RefSet,s=10;return{name:"string"==typeof e.get("Name")?stringToPDFString(e.get("Name")):null,creator:"string"==typeof e.get("Creator")?stringToPDFString(e.get("Creator")):null,baseState:e.get("BaseState")instanceof Name?e.get("BaseState").name:null,on:parseOnOff(e.get("ON")),off:parseOnOff(e.get("OFF")),order:parseOrder(e.get("Order")),groups:null}}setActualNumPages(e=null){this._actualNumPages=e}get hasActualNumPages(){return null!==this._actualNumPages}get _pagesCount(){const e=this.toplevelPagesDict.get("Count");if(!Number.isInteger(e))throw new FormatError("Page count in top-level pages dictionary is not an integer.");return shadow(this,"_pagesCount",e)}get numPages(){return this.hasActualNumPages?this._actualNumPages:this._pagesCount}get destinations(){const e=this._readDests(),t=Object.create(null);if(e instanceof NameTree)for(const[i,a]of e.getAll()){const e=fetchDest(a);e&&(t[stringToPDFString(i)]=e)}else e instanceof Dict&&e.forEach((function(e,i){const a=fetchDest(i);a&&(t[e]=a)}));return shadow(this,"destinations",t)}getDestination(e){const t=this._readDests();if(t instanceof NameTree){const i=fetchDest(t.get(e));if(i)return i;const a=this.destinations[e];if(a){warn(`Found "${e}" at an incorrect position in the NameTree.`);return a}}else if(t instanceof Dict){const i=fetchDest(t.get(e));if(i)return i}return null}_readDests(){const e=this._catDict.get("Names");return e?.has("Dests")?new NameTree(e.getRaw("Dests"),this.xref):this._catDict.has("Dests")?this._catDict.get("Dests"):void 0}get pageLabels(){let e=null;try{e=this._readPageLabels()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read page labels.")}return shadow(this,"pageLabels",e)}_readPageLabels(){const e=this._catDict.getRaw("PageLabels");if(!e)return null;const t=new Array(this.numPages);let i=null,a="";const s=new NumberTree(e,this.xref).getAll();let r="",n=1;for(let e=0,g=this.numPages;e<g;e++){const g=s.get(e);if(void 0!==g){if(!(g instanceof Dict))throw new FormatError("PageLabel is not a dictionary.");if(g.has("Type")&&!isName(g.get("Type"),"PageLabel"))throw new FormatError("Invalid type in PageLabel dictionary.");if(g.has("S")){const e=g.get("S");if(!(e instanceof Name))throw new FormatError("Invalid style in PageLabel dictionary.");i=e.name}else i=null;if(g.has("P")){const e=g.get("P");if("string"!=typeof e)throw new FormatError("Invalid prefix in PageLabel dictionary.");a=stringToPDFString(e)}else a="";if(g.has("St")){const e=g.get("St");if(!(Number.isInteger(e)&&e>=1))throw new FormatError("Invalid start in PageLabel dictionary.");n=e}else n=1}switch(i){case"D":r=n;break;case"R":case"r":r=toRomanNumerals(n,"r"===i);break;case"A":case"a":const e=26,t="a"===i?97:65,a=n-1;r=String.fromCharCode(t+a%e).repeat(Math.floor(a/e)+1);break;default:if(i)throw new FormatError(`Invalid style "${i}" in PageLabel dictionary.`);r=""}t[e]=a+r;n++}return t}get pageLayout(){const e=this._catDict.get("PageLayout");let t="";if(e instanceof Name)switch(e.name){case"SinglePage":case"OneColumn":case"TwoColumnLeft":case"TwoColumnRight":case"TwoPageLeft":case"TwoPageRight":t=e.name}return shadow(this,"pageLayout",t)}get pageMode(){const e=this._catDict.get("PageMode");let t="UseNone";if(e instanceof Name)switch(e.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"FullScreen":case"UseOC":case"UseAttachments":t=e.name}return shadow(this,"pageMode",t)}get viewerPreferences(){const e=this._catDict.get("ViewerPreferences");if(!(e instanceof Dict))return shadow(this,"viewerPreferences",null);let t=null;for(const i of e.getKeys()){const a=e.get(i);let s;switch(i){case"HideToolbar":case"HideMenubar":case"HideWindowUI":case"FitWindow":case"CenterWindow":case"DisplayDocTitle":case"PickTrayByPDFSize":"boolean"==typeof a&&(s=a);break;case"NonFullScreenPageMode":if(a instanceof Name)switch(a.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"UseOC":s=a.name;break;default:s="UseNone"}break;case"Direction":if(a instanceof Name)switch(a.name){case"L2R":case"R2L":s=a.name;break;default:s="L2R"}break;case"ViewArea":case"ViewClip":case"PrintArea":case"PrintClip":if(a instanceof Name)switch(a.name){case"MediaBox":case"CropBox":case"BleedBox":case"TrimBox":case"ArtBox":s=a.name;break;default:s="CropBox"}break;case"PrintScaling":if(a instanceof Name)switch(a.name){case"None":case"AppDefault":s=a.name;break;default:s="AppDefault"}break;case"Duplex":if(a instanceof Name)switch(a.name){case"Simplex":case"DuplexFlipShortEdge":case"DuplexFlipLongEdge":s=a.name;break;default:s="None"}break;case"PrintPageRange":if(Array.isArray(a)&&a.length%2==0){a.every(((e,t,i)=>Number.isInteger(e)&&e>0&&(0===t||e>=i[t-1])&&e<=this.numPages))&&(s=a)}break;case"NumCopies":Number.isInteger(a)&&a>0&&(s=a);break;default:warn(`Ignoring non-standard key in ViewerPreferences: ${i}.`);continue}if(void 0!==s){t||(t=Object.create(null));t[i]=s}else warn(`Bad value, for key "${i}", in ViewerPreferences: ${a}.`)}return shadow(this,"viewerPreferences",t)}get openAction(){const e=this._catDict.get("OpenAction"),t=Object.create(null);if(e instanceof Dict){const i=new Dict(this.xref);i.set("A",e);const a={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:i,resultObj:a});Array.isArray(a.dest)?t.dest=a.dest:a.action&&(t.action=a.action)}else Array.isArray(e)&&(t.dest=e);return shadow(this,"openAction",objectSize(t)>0?t:null)}get attachments(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("EmbeddedFiles")){const i=new NameTree(e.getRaw("EmbeddedFiles"),this.xref);for(const[e,a]of i.getAll()){const i=new FileSpec(a,this.xref);t||(t=Object.create(null));t[stringToPDFString(e)]=i.serializable}}return shadow(this,"attachments",t)}get xfaImages(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("XFAImages")){const i=new NameTree(e.getRaw("XFAImages"),this.xref);for(const[e,a]of i.getAll()){t||(t=new Dict(this.xref));t.set(stringToPDFString(e),a)}}return shadow(this,"xfaImages",t)}_collectJavaScript(){const e=this._catDict.get("Names");let t=null;function appendIfJavaScriptDict(e,i){if(!(i instanceof Dict))return;if(!isName(i.get("S"),"JavaScript"))return;let a=i.get("JS");if(a instanceof BaseStream)a=a.getString();else if("string"!=typeof a)return;a=stringToPDFString(a).replaceAll("\0","");a&&(t||=new Map).set(e,a)}if(e instanceof Dict&&e.has("JavaScript")){const t=new NameTree(e.getRaw("JavaScript"),this.xref);for(const[e,i]of t.getAll())appendIfJavaScriptDict(stringToPDFString(e),i)}const i=this._catDict.get("OpenAction");i&&appendIfJavaScriptDict("OpenAction",i);return t}get jsActions(){const e=this._collectJavaScript();let t=collectActions(this.xref,this._catDict,fA);if(e){t||=Object.create(null);for(const[i,a]of e)i in t?t[i].push(a):t[i]=[a]}return shadow(this,"jsActions",t)}async fontFallback(e,t){const i=await Promise.all(this.fontCache);for(const a of i)if(a.loadedName===e){a.fallback(t);return}}async cleanup(e=!1){clearGlobalCaches();this.globalImageCache.clear(e);this.pageKidsCountCache.clear();this.pageIndexCache.clear();this.pageDictCache.clear();this.nonBlendModesSet.clear();const t=await Promise.all(this.fontCache);for(const{dict:e}of t)delete e.cacheKey;this.fontCache.clear();this.builtInCMapCache.clear();this.standardFontDataCache.clear();this.systemFontCache.clear()}async getPageDict(e){const t=[this.toplevelPagesDict],i=new RefSet,a=this._catDict.getRaw("Pages");a instanceof Ref&&i.put(a);const s=this.xref,r=this.pageKidsCountCache,n=this.pageIndexCache,g=this.pageDictCache;let o=0;for(;t.length;){const a=t.pop();if(a instanceof Ref){const c=r.get(a);if(c>=0&&o+c<=e){o+=c;continue}if(i.has(a))throw new FormatError("Pages tree contains circular reference.");i.put(a);const C=await(g.get(a)||s.fetchAsync(a));if(C instanceof Dict){let t=C.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!C.has("Kids")){r.has(a)||r.put(a,1);n.has(a)||n.put(a,o);if(o===e)return[C,a];o++;continue}}t.push(C);continue}if(!(a instanceof Dict))throw new FormatError("Page dictionary kid reference points to wrong type of object.");const{objId:c}=a;let C=a.getRaw("Count");C instanceof Ref&&(C=await s.fetchAsync(C));if(Number.isInteger(C)&&C>=0){c&&!r.has(c)&&r.put(c,C);if(o+C<=e){o+=C;continue}}let h=a.getRaw("Kids");h instanceof Ref&&(h=await s.fetchAsync(h));if(!Array.isArray(h)){let t=a.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!a.has("Kids")){if(o===e)return[a,null];o++;continue}throw new FormatError("Page dictionary kids object is not an array.")}for(let e=h.length-1;e>=0;e--){const i=h[e];t.push(i);a===this.toplevelPagesDict&&i instanceof Ref&&!g.has(i)&&g.put(i,s.fetchAsync(i))}}throw new Error(`Page index ${e} not found.`)}async getAllPageDicts(e=!1){const{ignoreErrors:t}=this.pdfManager.evaluatorOptions,i=[{currentNode:this.toplevelPagesDict,posInKids:0}],a=new RefSet,s=this._catDict.getRaw("Pages");s instanceof Ref&&a.put(s);const r=new Map,n=this.xref,g=this.pageIndexCache;let o=0;function addPageDict(e,t){t&&!g.has(t)&&g.put(t,o);r.set(o++,[e,t])}function addPageError(i){if(i instanceof XRefEntryException&&!e)throw i;if(e&&t&&0===o){warn(`getAllPageDicts - Skipping invalid first page: "${i}".`);i=Dict.empty}r.set(o++,[i,null])}for(;i.length>0;){const e=i.at(-1),{currentNode:t,posInKids:s}=e;let r=t.getRaw("Kids");if(r instanceof Ref)try{r=await n.fetchAsync(r)}catch(e){addPageError(e);break}if(!Array.isArray(r)){addPageError(new FormatError("Page dictionary kids object is not an array."));break}if(s>=r.length){i.pop();continue}const g=r[s];let o;if(g instanceof Ref){if(a.has(g)){addPageError(new FormatError("Pages tree contains circular reference."));break}a.put(g);try{o=await n.fetchAsync(g)}catch(e){addPageError(e);break}}else o=g;if(!(o instanceof Dict)){addPageError(new FormatError("Page dictionary kid reference points to wrong type of object."));break}let c=o.getRaw("Type");if(c instanceof Ref)try{c=await n.fetchAsync(c)}catch(e){addPageError(e);break}isName(c,"Page")||!o.has("Kids")?addPageDict(o,g instanceof Ref?g:null):i.push({currentNode:o,posInKids:0});e.posInKids++}return r}getPageIndex(e){const t=this.pageIndexCache.get(e);if(void 0!==t)return Promise.resolve(t);const i=this.xref;let a=0;const next=t=>function pagesBeforeRef(t){let a,s=0;return i.fetchAsync(t).then((function(i){if(isRefsEqual(t,e)&&!isDict(i,"Page")&&!(i instanceof Dict&&!i.has("Type")&&i.has("Contents")))throw new FormatError("The reference does not point to a /Page dictionary.");if(!i)return null;if(!(i instanceof Dict))throw new FormatError("Node must be a dictionary.");a=i.getRaw("Parent");return i.getAsync("Parent")})).then((function(e){if(!e)return null;if(!(e instanceof Dict))throw new FormatError("Parent must be a dictionary.");return e.getAsync("Kids")})).then((function(e){if(!e)return null;const r=[];let n=!1;for(const a of e){if(!(a instanceof Ref))throw new FormatError("Kid must be a reference.");if(isRefsEqual(a,t)){n=!0;break}r.push(i.fetchAsync(a).then((function(e){if(!(e instanceof Dict))throw new FormatError("Kid node must be a dictionary.");e.has("Count")?s+=e.get("Count"):s++})))}if(!n)throw new FormatError("Kid reference not found in parent's kids.");return Promise.all(r).then((function(){return[s,a]}))}))}(t).then((t=>{if(!t){this.pageIndexCache.put(e,a);return a}const[i,s]=t;a+=i;return next(s)}));return next(e)}get baseUrl(){const e=this._catDict.get("URI");if(e instanceof Dict){const t=e.get("Base");if("string"==typeof t){const e=createValidAbsoluteUrl(t,null,{tryConvertEncoding:!0});if(e)return shadow(this,"baseUrl",e.href)}}return shadow(this,"baseUrl",this.pdfManager.docBaseUrl)}static parseDestDictionary({destDict:e,resultObj:t,docBaseUrl:i=null,docAttachments:a=null}){if(!(e instanceof Dict)){warn("parseDestDictionary: `destDict` must be a dictionary.");return}let s,r,n=e.get("A");if(!(n instanceof Dict))if(e.has("Dest"))n=e.get("Dest");else{n=e.get("AA");n instanceof Dict&&(n.has("D")?n=n.get("D"):n.has("U")&&(n=n.get("U")))}if(n instanceof Dict){const e=n.get("S");if(!(e instanceof Name)){warn("parseDestDictionary: Invalid type in Action dictionary.");return}const i=e.name;switch(i){case"ResetForm":const e=n.get("Flags"),g=0==(1&("number"==typeof e?e:0)),o=[],c=[];for(const e of n.get("Fields")||[])e instanceof Ref?c.push(e.toString()):"string"==typeof e&&o.push(stringToPDFString(e));t.resetForm={fields:o,refs:c,include:g};break;case"URI":s=n.get("URI");s instanceof Name&&(s="/"+s.name);break;case"GoTo":r=n.get("D");break;case"Launch":case"GoToR":const C=n.get("F");if(C instanceof Dict){const e=new FileSpec(C,null,!0),{rawFilename:t}=e.serializable;s=t}else"string"==typeof C&&(s=C);const h=fetchRemoteDest(n);h&&"string"==typeof s&&(s=s.split("#",1)[0]+"#"+h);const l=n.get("NewWindow");"boolean"==typeof l&&(t.newWindow=l);break;case"GoToE":const Q=n.get("T");let E;if(a&&Q instanceof Dict){const e=Q.get("R"),t=Q.get("N");isName(e,"C")&&"string"==typeof t&&(E=a[stringToPDFString(t)])}if(E){t.attachment=E;const e=fetchRemoteDest(n);e&&(t.attachmentDest=e)}else warn('parseDestDictionary - unimplemented "GoToE" action.');break;case"Named":const u=n.get("N");u instanceof Name&&(t.action=u.name);break;case"SetOCGState":const d=n.get("State"),f=n.get("PreserveRB");if(!Array.isArray(d)||0===d.length)break;const p=[];for(const e of d)if(e instanceof Name)switch(e.name){case"ON":case"OFF":case"Toggle":p.push(e.name)}else e instanceof Ref&&p.push(e.toString());if(p.length!==d.length)break;t.setOCGState={state:p,preserveRB:"boolean"!=typeof f||f};break;case"JavaScript":const m=n.get("JS");let y;m instanceof BaseStream?y=m.getString():"string"==typeof m&&(y=m);const w=y&&recoverJsURL(stringToPDFString(y));if(w){s=w.url;t.newWindow=w.newWindow;break}default:if("JavaScript"===i||"SubmitForm"===i)break;warn(`parseDestDictionary - unsupported action: "${i}".`)}}else e.has("Dest")&&(r=e.get("Dest"));if("string"==typeof s){const e=createValidAbsoluteUrl(s,i,{addDefaultProtocol:!0,tryConvertEncoding:!0});e&&(t.url=e.href);t.unsafeUrl=s}if(r){r instanceof Name&&(r=r.name);"string"==typeof r?t.dest=stringToPDFString(r):isValidExplicitDest(r)&&(t.dest=r)}}}function addChildren(e,t){if(e instanceof Dict)e=e.getRawValues();else if(e instanceof BaseStream)e=e.dict.getRawValues();else if(!Array.isArray(e))return;for(const a of e)((i=a)instanceof Ref||i instanceof Dict||i instanceof BaseStream||Array.isArray(i))&&t.push(a);var i}class ObjectLoader{constructor(e,t,i){this.dict=e;this.keys=t;this.xref=i;this.refSet=null}async load(){if(this.xref.stream.isDataLoaded)return;const{keys:e,dict:t}=this;this.refSet=new RefSet;const i=[];for(const a of e){const e=t.getRaw(a);void 0!==e&&i.push(e)}return this._walk(i)}async _walk(e){const t=[],i=[];for(;e.length;){let a=e.pop();if(a instanceof Ref){if(this.refSet.has(a))continue;try{this.refSet.put(a);a=this.xref.fetch(a)}catch(e){if(!(e instanceof MissingDataException)){warn(`ObjectLoader._walk - requesting all data: "${e}".`);this.refSet=null;const{manager:t}=this.xref.stream;return t.requestAllChunks()}t.push(a);i.push({begin:e.begin,end:e.end})}}if(a instanceof BaseStream){const e=a.getBaseStreams();if(e){let s=!1;for(const t of e)if(!t.isDataLoaded){s=!0;i.push({begin:t.start,end:t.end})}s&&t.push(a)}}addChildren(a,e)}if(i.length){await this.xref.stream.manager.requestRanges(i);for(const e of t)e instanceof Ref&&this.refSet.remove(e);return this._walk(t)}this.refSet=null}}const Vs=Symbol(),zs=Symbol(),_s=Symbol(),$s=Symbol(),Ar=Symbol(),er=Symbol(),tr=Symbol(),ir=Symbol(),ar=Symbol(),sr=Symbol("content"),rr=Symbol("data"),nr=Symbol(),gr=Symbol("extra"),or=Symbol(),Ir=Symbol(),cr=Symbol(),Cr=Symbol(),hr=Symbol(),lr=Symbol(),Qr=Symbol(),Er=Symbol(),ur=Symbol(),dr=Symbol(),fr=Symbol(),pr=Symbol(),mr=Symbol(),yr=Symbol(),wr=Symbol(),Dr=Symbol(),br=Symbol(),Fr=Symbol(),Sr=Symbol(),kr=Symbol(),Rr=Symbol(),Nr=Symbol(),Gr=Symbol(),xr=Symbol(),Ur=Symbol(),Mr=Symbol(),Lr=Symbol(),Hr=Symbol(),Jr=Symbol(),Yr=Symbol(),vr=Symbol(),Kr=Symbol(),Tr=Symbol("namespaceId"),qr=Symbol("nodeName"),Or=Symbol(),Pr=Symbol(),Wr=Symbol(),jr=Symbol(),Xr=Symbol(),Zr=Symbol(),Vr=Symbol(),zr=Symbol(),_r=Symbol("root"),$r=Symbol(),An=Symbol(),en=Symbol(),tn=Symbol(),an=Symbol(),sn=Symbol(),rn=Symbol(),nn=Symbol(),gn=Symbol(),on=Symbol(),In=Symbol(),cn=Symbol("uid"),Cn=Symbol(),hn={config:{id:0,check:e=>e.startsWith("http://www.xfa.org/schema/xci/")},connectionSet:{id:1,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-connection-set/")},datasets:{id:2,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-data/")},form:{id:3,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-form/")},localeSet:{id:4,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-locale-set/")},pdf:{id:5,check:e=>"http://ns.adobe.com/xdp/pdf/"===e},signature:{id:6,check:e=>"http://www.w3.org/2000/09/xmldsig#"===e},sourceSet:{id:7,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-source-set/")},stylesheet:{id:8,check:e=>"http://www.w3.org/1999/XSL/Transform"===e},template:{id:9,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-template/")},xdc:{id:10,check:e=>e.startsWith("http://www.xfa.org/schema/xdc/")},xdp:{id:11,check:e=>"http://ns.adobe.com/xdp/"===e},xfdf:{id:12,check:e=>"http://ns.adobe.com/xfdf/"===e},xhtml:{id:13,check:e=>"http://www.w3.org/1999/xhtml"===e},xmpmeta:{id:14,check:e=>"http://ns.adobe.com/xmpmeta/"===e}},Bn={pt:e=>e,cm:e=>e/2.54*72,mm:e=>e/25.4*72,in:e=>72*e,px:e=>e},ln=/([+-]?\d+\.?\d*)(.*)/;function stripQuotes(e){return e.startsWith("'")||e.startsWith('"')?e.slice(1,-1):e}function getInteger({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseInt(e,10);return!isNaN(a)&&i(a)?a:t}function getFloat({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseFloat(e);return!isNaN(a)&&i(a)?a:t}function getKeyword({data:e,defaultValue:t,validate:i}){return e&&i(e=e.trim())?e:t}function getStringOption(e,t){return getKeyword({data:e,defaultValue:t[0],validate:e=>t.includes(e)})}function getMeasurement(e,t="0"){t||="0";if(!e)return getMeasurement(t);const i=e.trim().match(ln);if(!i)return getMeasurement(t);const[,a,s]=i,r=parseFloat(a);if(isNaN(r))return getMeasurement(t);if(0===r)return 0;const n=Bn[s];return n?n(r):r}function getRatio(e){if(!e)return{num:1,den:1};const t=e.trim().split(/\s*:\s*/).map((e=>parseFloat(e))).filter((e=>!isNaN(e)));1===t.length&&t.push(1);if(0===t.length)return{num:1,den:1};const[i,a]=t;return{num:i,den:a}}function getRelevant(e){return e?e.trim().split(/\s+/).map((e=>({excluded:"-"===e[0],viewname:e.substring(1)}))):[]}class HTMLResult{static get FAILURE(){return shadow(this,"FAILURE",new HTMLResult(!1,null,null,null))}static get EMPTY(){return shadow(this,"EMPTY",new HTMLResult(!0,null,null,null))}constructor(e,t,i,a){this.success=e;this.html=t;this.bbox=i;this.breakNode=a}isBreak(){return!!this.breakNode}static breakNode(e){return new HTMLResult(!1,null,null,e)}static success(e,t=null){return new HTMLResult(!0,e,t,null)}}class FontFinder{constructor(e){this.fonts=new Map;this.cache=new Map;this.warned=new Set;this.defaultFont=null;this.add(e)}add(e,t=null){for(const t of e)this.addPdfFont(t);for(const e of this.fonts.values())e.regular||(e.regular=e.italic||e.bold||e.bolditalic);if(!t||0===t.size)return;const i=this.fonts.get("PdfJS-Fallback-PdfJS-XFA");for(const e of t)this.fonts.set(e,i)}addPdfFont(e){const t=e.cssFontInfo,i=t.fontFamily;let a=this.fonts.get(i);if(!a){a=Object.create(null);this.fonts.set(i,a);this.defaultFont||(this.defaultFont=a)}let s="";const r=parseFloat(t.fontWeight);0!==parseFloat(t.italicAngle)?s=r>=700?"bolditalic":"italic":r>=700&&(s="bold");if(!s){(e.name.includes("Bold")||e.psName?.includes("Bold"))&&(s="bold");(e.name.includes("Italic")||e.name.endsWith("It")||e.psName?.includes("Italic")||e.psName?.endsWith("It"))&&(s+="italic")}s||(s="regular");a[s]=e}getDefault(){return this.defaultFont}find(e,t=!0){let i=this.fonts.get(e)||this.cache.get(e);if(i)return i;const a=/,|-|_| |bolditalic|bold|italic|regular|it/gi;let s=e.replaceAll(a,"");i=this.fonts.get(s);if(i){this.cache.set(e,i);return i}s=s.toLowerCase();const r=[];for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t);if(0===r.length)for(const[,e]of this.fonts.entries())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(0===r.length){s=s.replaceAll(/psmt|mt/gi,"");for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t)}if(0===r.length)for(const e of this.fonts.values())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(r.length>=1){1!==r.length&&t&&warn(`XFA - Too many choices to guess the correct font: ${e}`);this.cache.set(e,r[0]);return r[0]}if(t&&!this.warned.has(e)){this.warned.add(e);warn(`XFA - Cannot find the font: ${e}`)}return null}}function selectFont(e,t){return"italic"===e.posture?"bold"===e.weight?t.bolditalic:t.italic:"bold"===e.weight?t.bold:t.regular}class FontInfo{constructor(e,t,i,a){this.lineHeight=i;this.paraMargin=t||{top:0,bottom:0,left:0,right:0};if(!e){[this.pdfFont,this.xfaFont]=this.defaultFont(a);return}this.xfaFont={typeface:e.typeface,posture:e.posture,weight:e.weight,size:e.size,letterSpacing:e.letterSpacing};const s=a.find(e.typeface);if(s){this.pdfFont=selectFont(e,s);this.pdfFont||([this.pdfFont,this.xfaFont]=this.defaultFont(a))}else[this.pdfFont,this.xfaFont]=this.defaultFont(a)}defaultFont(e){const t=e.find("Helvetica",!1)||e.find("Myriad Pro",!1)||e.find("Arial",!1)||e.getDefault();if(t?.regular){const e=t.regular;return[e,{typeface:e.cssFontInfo.fontFamily,posture:"normal",weight:"normal",size:10,letterSpacing:0}]}return[null,{typeface:"Courier",posture:"normal",weight:"normal",size:10,letterSpacing:0}]}}class FontSelector{constructor(e,t,i,a){this.fontFinder=a;this.stack=[new FontInfo(e,t,i,a)]}pushData(e,t,i){const a=this.stack.at(-1);for(const t of["typeface","posture","weight","size","letterSpacing"])e[t]||(e[t]=a.xfaFont[t]);for(const e of["top","bottom","left","right"])isNaN(t[e])&&(t[e]=a.paraMargin[e]);const s=new FontInfo(e,t,i||a.lineHeight,this.fontFinder);s.pdfFont||(s.pdfFont=a.pdfFont);this.stack.push(s)}popFont(){this.stack.pop()}topFont(){return this.stack.at(-1)}}class TextMeasure{constructor(e,t,i,a){this.glyphs=[];this.fontSelector=new FontSelector(e,t,i,a);this.extraHeight=0}pushData(e,t,i){this.fontSelector.pushData(e,t,i)}popFont(e){return this.fontSelector.popFont()}addPara(){const e=this.fontSelector.topFont();this.extraHeight+=e.paraMargin.top+e.paraMargin.bottom}addString(e){if(!e)return;const t=this.fontSelector.topFont(),i=t.xfaFont.size;if(t.pdfFont){const a=t.xfaFont.letterSpacing,s=t.pdfFont,r=s.lineHeight||1.2,n=t.lineHeight||Math.max(1.2,r)*i,g=r-(void 0===s.lineGap?.2:s.lineGap),o=Math.max(1,g)*i,c=i/1e3,C=s.defaultWidth||s.charsToGlyphs(" ")[0].width;for(const t of e.split(/[\u2029\n]/)){const e=s.encodeString(t).join(""),i=s.charsToGlyphs(e);for(const e of i){const t=e.width||C;this.glyphs.push([t*c+a,n,o,e.unicode,!1])}this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}else{for(const t of e.split(/[\u2029\n]/)){for(const e of t.split(""))this.glyphs.push([i,1.2*i,i,e,!1]);this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}}compute(e){let t=-1,i=0,a=0,s=0,r=0,n=0,g=!1,o=!0;for(let c=0,C=this.glyphs.length;c<C;c++){const[C,h,l,Q,E]=this.glyphs[c],u=" "===Q,d=o?l:h;if(E){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;o=!1}else if(u)if(r+C>e){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;g=!0;o=!1}else{n=Math.max(d,n);i=r;r+=C;t=c}else if(r+C>e){s+=n;n=d;if(-1!==t){c=t;a=Math.max(a,i);r=0;t=-1;i=0}else{a=Math.max(a,r);r=C}g=!0;o=!1}else{r+=C;n=Math.max(d,n)}}a=Math.max(a,r);s+=n+this.extraHeight;return{width:1.02*a,height:s,isBroken:g}}}const Qn=/^[^.[]+/,En=/^[^\]]+/,un={dot:0,dotDot:1,dotHash:2,dotBracket:3,dotParen:4},dn=new Map([["$data",(e,t)=>e.datasets?e.datasets.data:e],["$record",(e,t)=>(e.datasets?e.datasets.data:e)[pr]()[0]],["$template",(e,t)=>e.template],["$connectionSet",(e,t)=>e.connectionSet],["$form",(e,t)=>e.form],["$layout",(e,t)=>e.layout],["$host",(e,t)=>e.host],["$dataWindow",(e,t)=>e.dataWindow],["$event",(e,t)=>e.event],["!",(e,t)=>e.datasets],["$xfa",(e,t)=>e],["xfa",(e,t)=>e],["$",(e,t)=>t]]),fn=new WeakMap;function parseExpression(e,t,i=!0){let a=e.match(Qn);if(!a)return null;let[s]=a;const r=[{name:s,cacheName:"."+s,index:0,js:null,formCalc:null,operator:un.dot}];let n=s.length;for(;n<e.length;){const o=n;if("["===e.charAt(n++)){a=e.slice(n).match(En);if(!a){warn("XFA - Invalid index in SOM expression");return null}r.at(-1).index="*"===(g=(g=a[0]).trim())?1/0:parseInt(g,10)||0;n+=a[0].length+1;continue}let c;switch(e.charAt(n)){case".":if(!t)return null;n++;c=un.dotDot;break;case"#":n++;c=un.dotHash;break;case"[":if(i){warn("XFA - SOM expression contains a FormCalc subexpression which is not supported for now.");return null}c=un.dotBracket;break;case"(":if(i){warn("XFA - SOM expression contains a JavaScript subexpression which is not supported for now.");return null}c=un.dotParen;break;default:c=un.dot}a=e.slice(n).match(Qn);if(!a)break;[s]=a;n+=s.length;r.push({name:s,cacheName:e.slice(o,n),operator:c,index:0,js:null,formCalc:null})}var g;return r}function searchNode(e,t,i,a=!0,s=!0){const r=parseExpression(i,a);if(!r)return null;const n=dn.get(r[0].name);let g,o=0;if(n){g=!0;e=[n(e,t)];o=1}else{g=null===t;e=[t||e]}for(let i=r.length;o<i;o++){const{name:i,cacheName:a,operator:n,index:c}=r[o],C=[];for(const t of e){if(!t.isXFAObject)continue;let e,r;if(s){r=fn.get(t);if(!r){r=new Map;fn.set(t,r)}e=r.get(a)}if(!e){switch(n){case un.dot:e=t[Qr](i,!1);break;case un.dotDot:e=t[Qr](i,!0);break;case un.dotHash:e=t[lr](i);e=e.isXFAObjectArray?e.children:[e]}s&&r.set(a,e)}e.length>0&&C.push(e)}if(0!==C.length||g||0!==o)e=isFinite(c)?C.filter((e=>c<e.length)).map((e=>e[c])):C.flat();else{const i=t[Dr]();if(!(t=i))return null;o=-1;e=[t]}}return 0===e.length?null:e}function createDataNode(e,t,i){const a=parseExpression(i);if(!a)return null;if(a.some((e=>e.operator===un.dotDot)))return null;const s=dn.get(a[0].name);let r=0;if(s){e=s(e,t);r=1}else e=t||e;for(let t=a.length;r<t;r++){const{name:t,operator:i,index:s}=a[r];if(!isFinite(s)){a[r].index=0;return e.createNodes(a.slice(r))}let n;switch(i){case un.dot:n=e[Qr](t,!1);break;case un.dotDot:n=e[Qr](t,!0);break;case un.dotHash:n=e[lr](t);n=n.isXFAObjectArray?n.children:[n]}if(0===n.length)return e.createNodes(a.slice(r));if(!(s<n.length)){a[r].index=s-n.length;return e.createNodes(a.slice(r))}{const t=n[s];if(!t.isXFAObject){warn("XFA - Cannot create a node.");return null}e=t}}return null}const pn=Symbol(),mn=Symbol(),yn=Symbol(),wn=Symbol("_children"),Dn=Symbol(),bn=Symbol(),Fn=Symbol(),Sn=Symbol(),kn=Symbol(),Rn=Symbol(),Nn=Symbol(),Gn=Symbol(),xn=Symbol(),Un=Symbol("parent"),Mn=Symbol(),Ln=Symbol(),Hn=Symbol();let Jn=0;const Yn=hn.datasets.id;class XFAObject{constructor(e,t,i=!1){this[Tr]=e;this[qr]=t;this[Nn]=i;this[Un]=null;this[wn]=[];this[cn]=`${t}${Jn++}`;this[Fr]=null}get isXFAObject(){return!0}get isXFAObjectArray(){return!1}createNodes(e){let t=this,i=null;for(const{name:a,index:s}of e){for(let e=0,r=isFinite(s)?s:0;e<=r;e++){const e=t[Tr]===Yn?-1:t[Tr];i=new XmlObject(e,a);t[_s](i)}t=i}return i}[Pr](e){if(!this[Nn]||!this[Wr](e))return!1;const t=e[qr],i=this[t];if(!(i instanceof XFAObjectArray)){null!==i&&this[zr](i);this[t]=e;this[_s](e);return!0}if(i.push(e)){this[_s](e);return!0}let a="";this.id?a=` (id: ${this.id})`:this.name&&(a=` (name: ${this.name} ${this.h.value})`);warn(`XFA - node "${this[qr]}"${a} has already enough "${t}"!`);return!1}[Wr](e){return this.hasOwnProperty(e[qr])&&e[Tr]===this[Tr]}[Lr](){return!1}[Vs](){return!1}[Gr](){return!1}[xr](){return!1}[Zr](){this.para&&this[br]()[gr].paraStack.pop()}[Vr](){this[br]()[gr].paraStack.push(this.para)}[en](e){this.id&&this[Tr]===hn.template.id&&e.set(this.id,this)}[br](){return this[Fr].template}[Hr](){return!1}[Jr](){return!1}[_s](e){e[Un]=this;this[wn].push(e);!e[Fr]&&this[Fr]&&(e[Fr]=this[Fr])}[zr](e){const t=this[wn].indexOf(e);this[wn].splice(t,1)}[Sr](){return this.hasOwnProperty("value")}[an](e){}[jr](e){}[or](){}[Ar](e){delete this[Nn];if(this[tr]){e.clean(this[tr]);delete this[tr]}}[Rr](e){return this[wn].indexOf(e)}[Nr](e,t){t[Un]=this;this[wn].splice(e,0,t);!t[Fr]&&this[Fr]&&(t[Fr]=this[Fr])}[Yr](){return!this.name}[Kr](){return""}[rn](){return 0===this[wn].length?this[sr]:this[wn].map((e=>e[rn]())).join("")}get[yn](){const e=Object.getPrototypeOf(this);if(!e._attributes){const t=e._attributes=new Set;for(const e of Object.getOwnPropertyNames(this)){if(null===this[e]||this[e]instanceof XFAObject||this[e]instanceof XFAObjectArray)break;t.add(e)}}return shadow(this,yn,e._attributes)}[Mr](e){let t=this;for(;t;){if(t===e)return!0;t=t[Dr]()}return!1}[Dr](){return this[Un]}[wr](){return this[Dr]()}[pr](e=null){return e?this[e]:this[wn]}[nr](){const e=Object.create(null);this[sr]&&(e.$content=this[sr]);for(const t of Object.getOwnPropertyNames(this)){const i=this[t];null!==i&&(i instanceof XFAObject?e[t]=i[nr]():i instanceof XFAObjectArray?i.isEmpty()||(e[t]=i.dump()):e[t]=i)}return e}[In](){return null}[gn](){return HTMLResult.EMPTY}*[mr](){for(const e of this[pr]())yield e}*[Sn](e,t){for(const i of this[mr]())if(!e||t===e.has(i[qr])){const e=this[hr](),t=i[gn](e);t.success||(this[gr].failingNode=i);yield t}}[Ir](){return null}[zs](e,t){this[gr].children.push(e)}[hr](){}[$s]({filter:e=null,include:t=!0}){if(this[gr].generator){const e=this[hr](),t=this[gr].failingNode[gn](e);if(!t.success)return t;t.html&&this[zs](t.html,t.bbox);delete this[gr].failingNode}else this[gr].generator=this[Sn](e,t);for(;;){const e=this[gr].generator.next();if(e.done)break;const t=e.value;if(!t.success)return t;t.html&&this[zs](t.html,t.bbox)}this[gr].generator=null;return HTMLResult.EMPTY}[tn](e){this[Ln]=new Set(Object.keys(e))}[Rn](e){const t=this[yn],i=this[Ln];return[...e].filter((e=>t.has(e)&&!i.has(e)))}[$r](e,t=new Set){for(const i of this[wn])i[Mn](e,t)}[Mn](e,t){const i=this[kn](e,t);i?this[pn](i,e,t):this[$r](e,t)}[kn](e,t){const{use:i,usehref:a}=this;if(!i&&!a)return null;let s=null,r=null,n=null,g=i;if(a){g=a;a.startsWith("#som(")&&a.endsWith(")")?r=a.slice(5,-1):a.startsWith(".#som(")&&a.endsWith(")")?r=a.slice(6,-1):a.startsWith("#")?n=a.slice(1):a.startsWith(".#")&&(n=a.slice(2))}else i.startsWith("#")?n=i.slice(1):r=i;this.use=this.usehref="";if(n)s=e.get(n);else{s=searchNode(e.get(_r),this,r,!0,!1);s&&(s=s[0])}if(!s){warn(`XFA - Invalid prototype reference: ${g}.`);return null}if(s[qr]!==this[qr]){warn(`XFA - Incompatible prototype: ${s[qr]} !== ${this[qr]}.`);return null}if(t.has(s)){warn("XFA - Cycle detected in prototypes use.");return null}t.add(s);const o=s[kn](e,t);o&&s[pn](o,e,t);s[$r](e,t);t.delete(s);return s}[pn](e,t,i){if(i.has(e)){warn("XFA - Cycle detected in prototypes use.");return}!this[sr]&&e[sr]&&(this[sr]=e[sr]);new Set(i).add(e);for(const t of this[Rn](e[Ln])){this[t]=e[t];this[Ln]&&this[Ln].add(t)}for(const a of Object.getOwnPropertyNames(this)){if(this[yn].has(a))continue;const s=this[a],r=e[a];if(s instanceof XFAObjectArray){for(const e of s[wn])e[Mn](t,i);for(let a=s[wn].length,n=r[wn].length;a<n;a++){const r=e[wn][a][ir]();if(!s.push(r))break;r[Un]=this;this[wn].push(r);r[Mn](t,i)}}else if(null===s){if(null!==r){const e=r[ir]();e[Un]=this;this[a]=e;this[wn].push(e);e[Mn](t,i)}}else{s[$r](t,i);r&&s[pn](r,t,i)}}}static[Dn](e){return Array.isArray(e)?e.map((e=>XFAObject[Dn](e))):"object"==typeof e&&null!==e?Object.assign({},e):e}[ir](){const e=Object.create(Object.getPrototypeOf(this));for(const t of Object.getOwnPropertySymbols(this))try{e[t]=this[t]}catch{shadow(e,t,this[t])}e[cn]=`${e[qr]}${Jn++}`;e[wn]=[];for(const t of Object.getOwnPropertyNames(this)){if(this[yn].has(t)){e[t]=XFAObject[Dn](this[t]);continue}const i=this[t];e[t]=i instanceof XFAObjectArray?new XFAObjectArray(i[Gn]):null}for(const t of this[wn]){const i=t[qr],a=t[ir]();e[wn].push(a);a[Un]=e;null===e[i]?e[i]=a:e[i][wn].push(a)}return e}[pr](e=null){return e?this[wn].filter((t=>t[qr]===e)):this[wn]}[lr](e){return this[e]}[Qr](e,t,i=!0){return Array.from(this[Er](e,t,i))}*[Er](e,t,i=!0){if("parent"!==e){for(const i of this[wn]){i[qr]===e&&(yield i);i.name===e&&(yield i);(t||i[Yr]())&&(yield*i[Er](e,t,!1))}i&&this[yn].has(e)&&(yield new XFAAttribute(this,e,this[e]))}else yield this[Un]}}class XFAObjectArray{constructor(e=1/0){this[Gn]=e;this[wn]=[]}get isXFAObject(){return!1}get isXFAObjectArray(){return!0}push(e){if(this[wn].length<=this[Gn]){this[wn].push(e);return!0}warn(`XFA - node "${e[qr]}" accepts no more than ${this[Gn]} children`);return!1}isEmpty(){return 0===this[wn].length}dump(){return 1===this[wn].length?this[wn][0][nr]():this[wn].map((e=>e[nr]()))}[ir](){const e=new XFAObjectArray(this[Gn]);e[wn]=this[wn].map((e=>e[ir]()));return e}get children(){return this[wn]}clear(){this[wn].length=0}}class XFAAttribute{constructor(e,t,i){this[Un]=e;this[qr]=t;this[sr]=i;this[ar]=!1;this[cn]="attribute"+Jn++}[Dr](){return this[Un]}[Ur](){return!0}[ur](){return this[sr].trim()}[an](e){e=e.value||"";this[sr]=e.toString()}[rn](){return this[sr]}[Mr](e){return this[Un]===e||this[Un][Mr](e)}}class XmlObject extends XFAObject{constructor(e,t,i={}){super(e,t);this[sr]="";this[bn]=null;if("#text"!==t){const e=new Map;this[mn]=e;for(const[t,a]of Object.entries(i))e.set(t,new XFAAttribute(this,t,a));if(i.hasOwnProperty(Or)){const e=i[Or].xfa.dataNode;void 0!==e&&("dataGroup"===e?this[bn]=!1:"dataValue"===e&&(this[bn]=!0))}}this[ar]=!1}[on](e){const t=this[qr];if("#text"===t){e.push(encodeToXmlString(this[sr]));return}const i=utf8StringToString(t),a=this[Tr]===Yn?"xfa:":"";e.push(`<${a}${i}`);for(const[t,i]of this[mn].entries()){const a=utf8StringToString(t);e.push(` ${a}="${encodeToXmlString(i[sr])}"`)}null!==this[bn]&&(this[bn]?e.push(' xfa:dataNode="dataValue"'):e.push(' xfa:dataNode="dataGroup"'));if(this[sr]||0!==this[wn].length){e.push(">");if(this[sr])"string"==typeof this[sr]?e.push(encodeToXmlString(this[sr])):this[sr][on](e);else for(const t of this[wn])t[on](e);e.push(`</${a}${i}>`)}else e.push("/>")}[Pr](e){if(this[sr]){const e=new XmlObject(this[Tr],"#text");this[_s](e);e[sr]=this[sr];this[sr]=""}this[_s](e);return!0}[jr](e){this[sr]+=e}[or](){if(this[sr]&&this[wn].length>0){const e=new XmlObject(this[Tr],"#text");this[_s](e);e[sr]=this[sr];delete this[sr]}}[gn](){return"#text"===this[qr]?HTMLResult.success({name:"#text",value:this[sr]}):HTMLResult.EMPTY}[pr](e=null){return e?this[wn].filter((t=>t[qr]===e)):this[wn]}[Cr](){return this[mn]}[lr](e){const t=this[mn].get(e);return void 0!==t?t:this[pr](e)}*[Er](e,t){const i=this[mn].get(e);i&&(yield i);for(const i of this[wn]){i[qr]===e&&(yield i);t&&(yield*i[Er](e,t))}}*[cr](e,t){const i=this[mn].get(e);!i||t&&i[ar]||(yield i);for(const i of this[wn])yield*i[cr](e,t)}*[fr](e,t,i){for(const a of this[wn]){a[qr]!==e||i&&a[ar]||(yield a);t&&(yield*a[fr](e,t,i))}}[Ur](){return null===this[bn]?0===this[wn].length||this[wn][0][Tr]===hn.xhtml.id:this[bn]}[ur](){return null===this[bn]?0===this[wn].length?this[sr].trim():this[wn][0][Tr]===hn.xhtml.id?this[wn][0][rn]().trim():null:this[sr].trim()}[an](e){e=e.value||"";this[sr]=e.toString()}[nr](e=!1){const t=Object.create(null);e&&(t.$ns=this[Tr]);this[sr]&&(t.$content=this[sr]);t.$name=this[qr];t.children=[];for(const i of this[wn])t.children.push(i[nr](e));t.attributes=Object.create(null);for(const[e,i]of this[mn])t.attributes[e]=i[sr];return t}}class ContentObject extends XFAObject{constructor(e,t){super(e,t);this[sr]=""}[jr](e){this[sr]+=e}[or](){}}class OptionObject extends ContentObject{constructor(e,t,i){super(e,t);this[xn]=i}[or](){this[sr]=getKeyword({data:this[sr],defaultValue:this[xn][0],validate:e=>this[xn].includes(e)})}[Ar](e){super[Ar](e);delete this[xn]}}class StringObject extends ContentObject{[or](){this[sr]=this[sr].trim()}}class IntegerObject extends ContentObject{constructor(e,t,i,a){super(e,t);this[Fn]=i;this[Hn]=a}[or](){this[sr]=getInteger({data:this[sr],defaultValue:this[Fn],validate:this[Hn]})}[Ar](e){super[Ar](e);delete this[Fn];delete this[Hn]}}class Option01 extends IntegerObject{constructor(e,t){super(e,t,0,(e=>1===e))}}class Option10 extends IntegerObject{constructor(e,t){super(e,t,1,(e=>0===e))}}function measureToString(e){return"string"==typeof e?"0px":Number.isInteger(e)?`${e}px`:`${e.toFixed(2)}px`}const vn={anchorType(e,t){const i=e[wr]();if(i&&(!i.layout||"position"===i.layout)){"transform"in t||(t.transform="");switch(e.anchorType){case"bottomCenter":t.transform+="translate(-50%, -100%)";break;case"bottomLeft":t.transform+="translate(0,-100%)";break;case"bottomRight":t.transform+="translate(-100%,-100%)";break;case"middleCenter":t.transform+="translate(-50%,-50%)";break;case"middleLeft":t.transform+="translate(0,-50%)";break;case"middleRight":t.transform+="translate(-100%,-50%)";break;case"topCenter":t.transform+="translate(-50%,0)";break;case"topRight":t.transform+="translate(-100%,0)"}}},dimensions(e,t){const i=e[wr]();let a=e.w;const s=e.h;if(i.layout?.includes("row")){const t=i[gr],s=e.colSpan;let r;if(-1===s){r=t.columnWidths.slice(t.currentColumn).reduce(((e,t)=>e+t),0);t.currentColumn=0}else{r=t.columnWidths.slice(t.currentColumn,t.currentColumn+s).reduce(((e,t)=>e+t),0);t.currentColumn=(t.currentColumn+e.colSpan)%t.columnWidths.length}isNaN(r)||(a=e.w=r)}t.width=""!==a?measureToString(a):"auto";t.height=""!==s?measureToString(s):"auto"},position(e,t){const i=e[wr]();if(!i?.layout||"position"===i.layout){t.position="absolute";t.left=measureToString(e.x);t.top=measureToString(e.y)}},rotate(e,t){if(e.rotate){"transform"in t||(t.transform="");t.transform+=`rotate(-${e.rotate}deg)`;t.transformOrigin="top left"}},presence(e,t){switch(e.presence){case"invisible":t.visibility="hidden";break;case"hidden":case"inactive":t.display="none"}},hAlign(e,t){if("para"===e[qr])switch(e.hAlign){case"justifyAll":t.textAlign="justify-all";break;case"radix":t.textAlign="left";break;default:t.textAlign=e.hAlign}else switch(e.hAlign){case"left":t.alignSelf="start";break;case"center":t.alignSelf="center";break;case"right":t.alignSelf="end"}},margin(e,t){e.margin&&(t.margin=e.margin[In]().margin)}};function setMinMaxDimensions(e,t){if("position"===e[wr]().layout){e.minW>0&&(t.minWidth=measureToString(e.minW));e.maxW>0&&(t.maxWidth=measureToString(e.maxW));e.minH>0&&(t.minHeight=measureToString(e.minH));e.maxH>0&&(t.maxHeight=measureToString(e.maxH))}}function layoutText(e,t,i,a,s,r){const n=new TextMeasure(t,i,a,s);"string"==typeof e?n.addString(e):e[Xr](n);return n.compute(r)}function layoutNode(e,t){let i=null,a=null,s=!1;if((!e.w||!e.h)&&e.value){let r=0,n=0;if(e.margin){r=e.margin.leftInset+e.margin.rightInset;n=e.margin.topInset+e.margin.bottomInset}let g=null,o=null;if(e.para){o=Object.create(null);g=""===e.para.lineHeight?null:e.para.lineHeight;o.top=""===e.para.spaceAbove?0:e.para.spaceAbove;o.bottom=""===e.para.spaceBelow?0:e.para.spaceBelow;o.left=""===e.para.marginLeft?0:e.para.marginLeft;o.right=""===e.para.marginRight?0:e.para.marginRight}let c=e.font;if(!c){const t=e[br]();let i=e[Dr]();for(;i&&i!==t;){if(i.font){c=i.font;break}i=i[Dr]()}}const C=(e.w||t.width)-r,h=e[Fr].fontFinder;if(e.value.exData&&e.value.exData[sr]&&"text/html"===e.value.exData.contentType){const t=layoutText(e.value.exData[sr],c,o,g,h,C);a=t.width;i=t.height;s=t.isBroken}else{const t=e.value[rn]();if(t){const e=layoutText(t,c,o,g,h,C);a=e.width;i=e.height;s=e.isBroken}}null===a||e.w||(a+=r);null===i||e.h||(i+=n)}return{w:a,h:i,isBroken:s}}function computeBbox(e,t,i){let a;if(""!==e.w&&""!==e.h)a=[e.x,e.y,e.w,e.h];else{if(!i)return null;let s=e.w;if(""===s){if(0===e.maxW){const t=e[wr]();s="position"===t.layout&&""!==t.w?0:e.minW}else s=Math.min(e.maxW,i.width);t.attributes.style.width=measureToString(s)}let r=e.h;if(""===r){if(0===e.maxH){const t=e[wr]();r="position"===t.layout&&""!==t.h?0:e.minH}else r=Math.min(e.maxH,i.height);t.attributes.style.height=measureToString(r)}a=[e.x,e.y,s,r]}return a}function fixDimensions(e){const t=e[wr]();if(t.layout?.includes("row")){const i=t[gr],a=e.colSpan;let s;s=-1===a?i.columnWidths.slice(i.currentColumn).reduce(((e,t)=>e+t),0):i.columnWidths.slice(i.currentColumn,i.currentColumn+a).reduce(((e,t)=>e+t),0);isNaN(s)||(e.w=s)}t.layout&&"position"!==t.layout&&(e.x=e.y=0);"table"===e.layout&&""===e.w&&Array.isArray(e.columnWidths)&&(e.w=e.columnWidths.reduce(((e,t)=>e+t),0))}function layoutClass(e){switch(e.layout){case"position":default:return"xfaPosition";case"lr-tb":return"xfaLrTb";case"rl-row":return"xfaRlRow";case"rl-tb":return"xfaRlTb";case"row":return"xfaRow";case"table":return"xfaTable";case"tb":return"xfaTb"}}function toStyle(e,...t){const i=Object.create(null);for(const a of t){const t=e[a];if(null!==t)if(vn.hasOwnProperty(a))vn[a](e,i);else if(t instanceof XFAObject){const e=t[In]();e?Object.assign(i,e):warn(`(DEBUG) - XFA - style for ${a} not implemented yet`)}}return i}function createWrapper(e,t){const{attributes:i}=t,{style:a}=i,s={name:"div",attributes:{class:["xfaWrapper"],style:Object.create(null)},children:[]};i.class.push("xfaWrapped");if(e.border){const{widths:i,insets:r}=e.border[gr];let n,g,o=r[0],c=r[3];const C=r[0]+r[2],h=r[1]+r[3];switch(e.border.hand){case"even":o-=i[0]/2;c-=i[3]/2;n=`calc(100% + ${(i[1]+i[3])/2-h}px)`;g=`calc(100% + ${(i[0]+i[2])/2-C}px)`;break;case"left":o-=i[0];c-=i[3];n=`calc(100% + ${i[1]+i[3]-h}px)`;g=`calc(100% + ${i[0]+i[2]-C}px)`;break;case"right":n=h?`calc(100% - ${h}px)`:"100%";g=C?`calc(100% - ${C}px)`:"100%"}const l=["xfaBorder"];isPrintOnly(e.border)&&l.push("xfaPrintOnly");const Q={name:"div",attributes:{class:l,style:{top:`${o}px`,left:`${c}px`,width:n,height:g}},children:[]};for(const e of["border","borderWidth","borderColor","borderRadius","borderStyle"])if(void 0!==a[e]){Q.attributes.style[e]=a[e];delete a[e]}s.children.push(Q,t)}else s.children.push(t);for(const e of["background","backgroundClip","top","left","width","height","minWidth","minHeight","maxWidth","maxHeight","transform","transformOrigin","visibility"])if(void 0!==a[e]){s.attributes.style[e]=a[e];delete a[e]}s.attributes.style.position="absolute"===a.position?"absolute":"relative";delete a.position;if(a.alignSelf){s.attributes.style.alignSelf=a.alignSelf;delete a.alignSelf}return s}function fixTextIndent(e){const t=getMeasurement(e.textIndent,"0px");if(t>=0)return;const i="padding"+("left"===("right"===e.textAlign?"right":"left")?"Left":"Right"),a=getMeasurement(e[i],"0px");e[i]=a-t+"px"}function setAccess(e,t){switch(e.access){case"nonInteractive":t.push("xfaNonInteractive");break;case"readOnly":t.push("xfaReadOnly");break;case"protected":t.push("xfaDisabled")}}function isPrintOnly(e){return e.relevant.length>0&&!e.relevant[0].excluded&&"print"===e.relevant[0].viewname}function getCurrentPara(e){const t=e[br]()[gr].paraStack;return t.length?t.at(-1):null}function setPara(e,t,i){if(i.attributes.class?.includes("xfaRich")){if(t){""===e.h&&(t.height="auto");""===e.w&&(t.width="auto")}const a=getCurrentPara(e);if(a){const e=i.attributes.style;e.display="flex";e.flexDirection="column";switch(a.vAlign){case"top":e.justifyContent="start";break;case"bottom":e.justifyContent="end";break;case"middle":e.justifyContent="center"}const t=a[In]();for(const[i,a]of Object.entries(t))i in e||(e[i]=a)}}}function setFontFamily(e,t,i,a){if(!i){delete a.fontFamily;return}const s=stripQuotes(e.typeface);a.fontFamily=`"${s}"`;const r=i.find(s);if(r){const{fontFamily:i}=r.regular.cssFontInfo;i!==s&&(a.fontFamily=`"${i}"`);const n=getCurrentPara(t);if(n&&""!==n.lineHeight)return;if(a.lineHeight)return;const g=selectFont(e,r);g&&(a.lineHeight=Math.max(1.2,g.lineHeight))}}function fixURL(e){const t=createValidAbsoluteUrl(e,null,{addDefaultProtocol:!0,tryConvertEncoding:!0});return t?t.href:null}function createLine(e,t){return{name:"div",attributes:{class:["lr-tb"===e.layout?"xfaLr":"xfaRl"]},children:t}}function flushHTML(e){if(!e[gr])return null;const t={name:"div",attributes:e[gr].attributes,children:e[gr].children};if(e[gr].failingNode){const i=e[gr].failingNode[Ir]();i&&(e.layout.endsWith("-tb")?t.children.push(createLine(e,[i])):t.children.push(i))}return 0===t.children.length?null:t}function addHTML(e,t,i){const a=e[gr],s=a.availableSpace,[r,n,g,o]=i;switch(e.layout){case"position":a.width=Math.max(a.width,r+g);a.height=Math.max(a.height,n+o);a.children.push(t);break;case"lr-tb":case"rl-tb":if(!a.line||1===a.attempt){a.line=createLine(e,[]);a.children.push(a.line);a.numberInLine=0}a.numberInLine+=1;a.line.children.push(t);if(0===a.attempt){a.currentWidth+=g;a.height=Math.max(a.height,a.prevHeight+o)}else{a.currentWidth=g;a.prevHeight=a.height;a.height+=o;a.attempt=0}a.width=Math.max(a.width,a.currentWidth);break;case"rl-row":case"row":{a.children.push(t);a.width+=g;a.height=Math.max(a.height,o);const e=measureToString(a.height);for(const t of a.children)t.attributes.style.height=e;break}case"table":case"tb":a.width=Math.min(s.width,Math.max(a.width,g));a.height+=o;a.children.push(t)}}function getAvailableSpace(e){const t=e[gr].availableSpace,i=e.margin?e.margin.topInset+e.margin.bottomInset:0,a=e.margin?e.margin.leftInset+e.margin.rightInset:0;switch(e.layout){case"lr-tb":case"rl-tb":return 0===e[gr].attempt?{width:t.width-a-e[gr].currentWidth,height:t.height-i-e[gr].prevHeight}:{width:t.width-a,height:t.height-i-e[gr].height};case"rl-row":case"row":return{width:e[gr].columnWidths.slice(e[gr].currentColumn).reduce(((e,t)=>e+t)),height:t.height-a};case"table":case"tb":return{width:t.width-a,height:t.height-i-e[gr].height};default:return t}}function checkDimensions(e,t){if(null===e[br]()[gr].firstUnsplittable)return!0;if(0===e.w||0===e.h)return!0;const i=e[wr](),a=i[gr]?.attempt||0,[,s,r,n]=function getTransformedBBox(e){let t,i,a=""===e.w?NaN:e.w,s=""===e.h?NaN:e.h,[r,n]=[0,0];switch(e.anchorType||""){case"bottomCenter":[r,n]=[a/2,s];break;case"bottomLeft":[r,n]=[0,s];break;case"bottomRight":[r,n]=[a,s];break;case"middleCenter":[r,n]=[a/2,s/2];break;case"middleLeft":[r,n]=[0,s/2];break;case"middleRight":[r,n]=[a,s/2];break;case"topCenter":[r,n]=[a/2,0];break;case"topRight":[r,n]=[a,0]}switch(e.rotate||0){case 0:[t,i]=[-r,-n];break;case 90:[t,i]=[-n,r];[a,s]=[s,-a];break;case 180:[t,i]=[r,n];[a,s]=[-a,-s];break;case 270:[t,i]=[n,-r];[a,s]=[-s,a]}return[e.x+t+Math.min(0,a),e.y+i+Math.min(0,s),Math.abs(a),Math.abs(s)]}(e);switch(i.layout){case"lr-tb":case"rl-tb":return 0===a?e[br]()[gr].noLayoutFailure?""!==e.w?Math.round(r-t.width)<=2:t.width>2:!(""!==e.h&&Math.round(n-t.height)>2)&&(""!==e.w?Math.round(r-t.width)<=2||0===i[gr].numberInLine&&t.height>2:t.width>2):!!e[br]()[gr].noLayoutFailure||!(""!==e.h&&Math.round(n-t.height)>2)&&((""===e.w||Math.round(r-t.width)<=2||!i[Jr]())&&t.height>2);case"table":case"tb":return!!e[br]()[gr].noLayoutFailure||(""===e.h||e[Hr]()?(""===e.w||Math.round(r-t.width)<=2||!i[Jr]())&&t.height>2:Math.round(n-t.height)<=2);case"position":if(e[br]()[gr].noLayoutFailure)return!0;if(""===e.h||Math.round(n+s-t.height)<=2)return!0;return n+s>e[br]()[gr].currentContentArea.h;case"rl-row":case"row":return!!e[br]()[gr].noLayoutFailure||(""===e.h||Math.round(n-t.height)<=2);default:return!0}}const Kn=hn.template.id,Tn="http://www.w3.org/2000/svg",qn=/^H(\d+)$/,On=new Set(["image/gif","image/jpeg","image/jpg","image/pjpeg","image/png","image/apng","image/x-png","image/bmp","image/x-ms-bmp","image/tiff","image/tif","application/octet-stream"]),Pn=[[[66,77],"image/bmp"],[[255,216,255],"image/jpeg"],[[73,73,42,0],"image/tiff"],[[77,77,0,42],"image/tiff"],[[71,73,70,56,57,97],"image/gif"],[[137,80,78,71,13,10,26,10],"image/png"]];function getBorderDims(e){if(!e||!e.border)return{w:0,h:0};const t=e.border[dr]();return t?{w:t.widths[0]+t.widths[2]+t.insets[0]+t.insets[2],h:t.widths[1]+t.widths[3]+t.insets[1]+t.insets[3]}:{w:0,h:0}}function hasMargin(e){return e.margin&&(e.margin.topInset||e.margin.rightInset||e.margin.bottomInset||e.margin.leftInset)}function _setValue(e,t){if(!e.value){const t=new Value({});e[_s](t);e.value=t}e.value[an](t)}function*getContainedChildren(e){for(const t of e[pr]())t instanceof SubformSet?yield*t[mr]():yield t}function isRequired(e){return"error"===e.validate?.nullTest}function setTabIndex(e){for(;e;){if(!e.traversal){e[sn]=e[Dr]()[sn];return}if(e[sn])return;let t=null;for(const i of e.traversal[pr]())if("next"===i.operation){t=i;break}if(!t||!t.ref){e[sn]=e[Dr]()[sn];return}const i=e[br]();e[sn]=++i[sn];const a=i[An](t.ref,e);if(!a)return;e=a[0]}}function applyAssist(e,t){const i=e.assist;if(i){const e=i[gn]();e&&(t.title=e);const a=i.role.match(qn);if(a){const e="heading",i=a[1];t.role=e;t["aria-level"]=i}}if("table"===e.layout)t.role="table";else if("row"===e.layout)t.role="row";else{const i=e[Dr]();"row"===i.layout&&(t.role="TH"===i.assist?.role?"columnheader":"cell")}}function ariaLabel(e){if(!e.assist)return null;const t=e.assist;return t.speak&&""!==t.speak[sr]?t.speak[sr]:t.toolTip?t.toolTip[sr]:null}function valueToHtml(e){return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:Object.create(null)},children:[{name:"span",attributes:{style:Object.create(null)},value:e}]})}function setFirstUnsplittable(e){const t=e[br]();if(null===t[gr].firstUnsplittable){t[gr].firstUnsplittable=e;t[gr].noLayoutFailure=!0}}function unsetFirstUnsplittable(e){const t=e[br]();t[gr].firstUnsplittable===e&&(t[gr].noLayoutFailure=!1)}function handleBreak(e){if(e[gr])return!1;e[gr]=Object.create(null);if("auto"===e.targetType)return!1;const t=e[br]();let i=null;if(e.target){i=t[An](e.target,e[Dr]());if(!i)return!1;i=i[0]}const{currentPageArea:a,currentContentArea:s}=t[gr];if("pageArea"===e.targetType){i instanceof PageArea||(i=null);if(e.startNew){e[gr].target=i||a;return!0}if(i&&i!==a){e[gr].target=i;return!0}return!1}i instanceof ContentArea||(i=null);const r=i&&i[Dr]();let n,g=r;if(e.startNew)if(i){const e=r.contentArea.children,t=e.indexOf(s),a=e.indexOf(i);-1!==t&&t<a&&(g=null);n=a-1}else n=a.contentArea.children.indexOf(s);else{if(!i||i===s)return!1;n=r.contentArea.children.indexOf(i)-1;g=r===a?null:r}e[gr].target=g;e[gr].index=n;return!0}function handleOverflow(e,t,i){const a=e[br](),s=a[gr].noLayoutFailure,r=t[wr];t[wr]=()=>e;a[gr].noLayoutFailure=!0;const n=t[gn](i);e[zs](n.html,n.bbox);a[gr].noLayoutFailure=s;t[wr]=r}class AppearanceFilter extends StringObject{constructor(e){super(Kn,"appearanceFilter");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Arc extends XFAObject{constructor(e){super(Kn,"arc",!0);this.circular=getInteger({data:e.circular,defaultValue:0,validate:e=>1===e});this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.startAngle=getFloat({data:e.startAngle,defaultValue:0,validate:e=>!0});this.sweepAngle=getFloat({data:e.sweepAngle,defaultValue:360,validate:e=>!0});this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null;this.fill=null}[gn](){const e=this.edge||new Edge({}),t=e[In](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[In]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;let a;const s={xmlns:Tn,style:{width:"100%",height:"100%",overflow:"visible"}};if(360===this.sweepAngle)a={name:"ellipse",attributes:{xmlns:Tn,cx:"50%",cy:"50%",rx:"50%",ry:"50%",style:i}};else{const e=this.startAngle*Math.PI/180,t=this.sweepAngle*Math.PI/180,r=this.sweepAngle>180?1:0,[n,g,o,c]=[50*(1+Math.cos(e)),50*(1-Math.sin(e)),50*(1+Math.cos(e+t)),50*(1-Math.sin(e+t))];a={name:"path",attributes:{xmlns:Tn,d:`M ${n} ${g} A 50 50 0 ${r} 0 ${o} ${c}`,vectorEffect:"non-scaling-stroke",style:i}};Object.assign(s,{viewBox:"0 0 100 100",preserveAspectRatio:"none"})}const r={name:"svg",children:[a],attributes:s};if(hasMargin(this[Dr]()[Dr]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[r]});r.attributes.style.position="absolute";return HTMLResult.success(r)}}class Area extends XFAObject{constructor(e){super(Kn,"area",!0);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null;this.area=new XFAObjectArray;this.draw=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[mr](){yield*getContainedChildren(this)}[Yr](){return!0}[xr](){return!0}[zs](e,t){const[i,a,s,r]=t;this[gr].width=Math.max(this[gr].width,i+s);this[gr].height=Math.max(this[gr].height,a+r);this[gr].children.push(e)}[hr](){return this[gr].availableSpace}[gn](e){const t=toStyle(this,"position"),i={style:t,id:this[cn],class:["xfaArea"]};isPrintOnly(this)&&i.class.push("xfaPrintOnly");this.name&&(i.xfaName=this.name);const a=[];this[gr]={children:a,width:0,height:0,availableSpace:e};const s=this[$s]({filter:new Set(["area","draw","field","exclGroup","subform","subformSet"]),include:!0});if(!s.success){if(s.isBreak())return s;delete this[gr];return HTMLResult.FAILURE}t.width=measureToString(this[gr].width);t.height=measureToString(this[gr].height);const r={name:"div",attributes:i,children:a},n=[this.x,this.y,this[gr].width,this[gr].height];delete this[gr];return HTMLResult.success(r,n)}}class Assist extends XFAObject{constructor(e){super(Kn,"assist",!0);this.id=e.id||"";this.role=e.role||"";this.use=e.use||"";this.usehref=e.usehref||"";this.speak=null;this.toolTip=null}[gn](){return this.toolTip?.[sr]||null}}class Barcode extends XFAObject{constructor(e){super(Kn,"barcode",!0);this.charEncoding=getKeyword({data:e.charEncoding?e.charEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.checksum=getStringOption(e.checksum,["none","1mod10","1mod10_1mod11","2mod10","auto"]);this.dataColumnCount=getInteger({data:e.dataColumnCount,defaultValue:-1,validate:e=>e>=0});this.dataLength=getInteger({data:e.dataLength,defaultValue:-1,validate:e=>e>=0});this.dataPrep=getStringOption(e.dataPrep,["none","flateCompress"]);this.dataRowCount=getInteger({data:e.dataRowCount,defaultValue:-1,validate:e=>e>=0});this.endChar=e.endChar||"";this.errorCorrectionLevel=getInteger({data:e.errorCorrectionLevel,defaultValue:-1,validate:e=>e>=0&&e<=8});this.id=e.id||"";this.moduleHeight=getMeasurement(e.moduleHeight,"5mm");this.moduleWidth=getMeasurement(e.moduleWidth,"0.25mm");this.printCheckDigit=getInteger({data:e.printCheckDigit,defaultValue:0,validate:e=>1===e});this.rowColumnRatio=getRatio(e.rowColumnRatio);this.startChar=e.startChar||"";this.textLocation=getStringOption(e.textLocation,["below","above","aboveEmbedded","belowEmbedded","none"]);this.truncate=getInteger({data:e.truncate,defaultValue:0,validate:e=>1===e});this.type=getStringOption(e.type?e.type.toLowerCase():"",["aztec","codabar","code2of5industrial","code2of5interleaved","code2of5matrix","code2of5standard","code3of9","code3of9extended","code11","code49","code93","code128","code128a","code128b","code128c","code128sscc","datamatrix","ean8","ean8add2","ean8add5","ean13","ean13add2","ean13add5","ean13pwcd","fim","logmars","maxicode","msi","pdf417","pdf417macro","plessey","postauscust2","postauscust3","postausreplypaid","postausstandard","postukrm4scc","postusdpbc","postusimb","postusstandard","postus5zip","qrcode","rfid","rss14","rss14expanded","rss14limited","rss14stacked","rss14stackedomni","rss14truncated","telepen","ucc128","ucc128random","ucc128sscc","upca","upcaadd2","upcaadd5","upcapwcd","upce","upceadd2","upceadd5","upcean2","upcean5","upsmaxicode"]);this.upsMode=getStringOption(e.upsMode,["usCarrier","internationalCarrier","secureSymbol","standardSymbol"]);this.use=e.use||"";this.usehref=e.usehref||"";this.wideNarrowRatio=getRatio(e.wideNarrowRatio);this.encrypt=null;this.extras=null}}class Bind extends XFAObject{constructor(e){super(Kn,"bind",!0);this.match=getStringOption(e.match,["once","dataRef","global","none"]);this.ref=e.ref||"";this.picture=null}}class BindItems extends XFAObject{constructor(e){super(Kn,"bindItems");this.connection=e.connection||"";this.labelRef=e.labelRef||"";this.ref=e.ref||"";this.valueRef=e.valueRef||""}}class Bookend extends XFAObject{constructor(e){super(Kn,"bookend");this.id=e.id||"";this.leader=e.leader||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}}class BooleanElement extends Option01{constructor(e){super(Kn,"boolean");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[gn](e){return valueToHtml(1===this[sr]?"1":"0")}}class Border extends XFAObject{constructor(e){super(Kn,"border",!0);this.break=getStringOption(e.break,["close","open"]);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.extras=null;this.fill=null;this.margin=null}[dr](){if(!this[gr]){const e=this.edge.children.slice();if(e.length<4){const t=e.at(-1)||new Edge({});for(let i=e.length;i<4;i++)e.push(t)}const t=e.map((e=>e.thickness)),i=[0,0,0,0];if(this.margin){i[0]=this.margin.topInset;i[1]=this.margin.rightInset;i[2]=this.margin.bottomInset;i[3]=this.margin.leftInset}this[gr]={widths:t,insets:i,edges:e}}return this[gr]}[In](){const{edges:e}=this[dr](),t=e.map((e=>{const t=e[In]();t.color||="#000000";return t})),i=Object.create(null);this.margin&&Object.assign(i,this.margin[In]());"visible"===this.fill?.presence&&Object.assign(i,this.fill[In]());if(this.corner.children.some((e=>0!==e.radius))){const e=this.corner.children.map((e=>e[In]()));if(2===e.length||3===e.length){const t=e.at(-1);for(let i=e.length;i<4;i++)e.push(t)}i.borderRadius=e.map((e=>e.radius)).join(" ")}switch(this.presence){case"invisible":case"hidden":i.borderStyle="";break;case"inactive":i.borderStyle="none";break;default:i.borderStyle=t.map((e=>e.style)).join(" ")}i.borderWidth=t.map((e=>e.width)).join(" ");i.borderColor=t.map((e=>e.color)).join(" ");return i}}class Break extends XFAObject{constructor(e){super(Kn,"break",!0);this.after=getStringOption(e.after,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.afterTarget=e.afterTarget||"";this.before=getStringOption(e.before,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.beforeTarget=e.beforeTarget||"";this.bookendLeader=e.bookendLeader||"";this.bookendTrailer=e.bookendTrailer||"";this.id=e.id||"";this.overflowLeader=e.overflowLeader||"";this.overflowTarget=e.overflowTarget||"";this.overflowTrailer=e.overflowTrailer||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class BreakAfter extends XFAObject{constructor(e){super(Kn,"breakAfter",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}}class BreakBefore extends XFAObject{constructor(e){super(Kn,"breakBefore",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}[gn](e){this[gr]={};return HTMLResult.FAILURE}}class Button extends XFAObject{constructor(e){super(Kn,"button",!0);this.highlight=getStringOption(e.highlight,["inverted","none","outline","push"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[gn](e){const t=this[Dr]()[Dr](),i={name:"button",attributes:{id:this[cn],class:["xfaButton"],style:{}},children:[]};for(const e of t.event.children){if("click"!==e.activity||!e.script)continue;const t=recoverJsURL(e.script[sr]);if(!t)continue;const a=fixURL(t.url);a&&i.children.push({name:"a",attributes:{id:"link"+this[cn],href:a,newWindow:t.newWindow,class:["xfaLink"],style:{}},children:[]})}return HTMLResult.success(i)}}class Calculate extends XFAObject{constructor(e){super(Kn,"calculate",!0);this.id=e.id||"";this.override=getStringOption(e.override,["disabled","error","ignore","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.script=null}}class Caption extends XFAObject{constructor(e){super(Kn,"caption",!0);this.id=e.id||"";this.placement=getStringOption(e.placement,["left","bottom","inline","right","top"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.reserve=Math.ceil(getMeasurement(e.reserve));this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.font=null;this.margin=null;this.para=null;this.value=null}[an](e){_setValue(this,e)}[dr](e){if(!this[gr]){let{width:t,height:i}=e;switch(this.placement){case"left":case"right":case"inline":t=this.reserve<=0?t:this.reserve;break;case"top":case"bottom":i=this.reserve<=0?i:this.reserve}this[gr]=layoutNode(this,{width:t,height:i})}return this[gr]}[gn](e){if(!this.value)return HTMLResult.EMPTY;this[Vr]();const t=this.value[gn](e).html;if(!t){this[Zr]();return HTMLResult.EMPTY}const i=this.reserve;if(this.reserve<=0){const{w:t,h:i}=this[dr](e);switch(this.placement){case"left":case"right":case"inline":this.reserve=t;break;case"top":case"bottom":this.reserve=i}}const a=[];"string"==typeof t?a.push({name:"#text",value:t}):a.push(t);const s=toStyle(this,"font","margin","visibility");switch(this.placement){case"left":case"right":this.reserve>0&&(s.width=measureToString(this.reserve));break;case"top":case"bottom":this.reserve>0&&(s.height=measureToString(this.reserve))}setPara(this,null,t);this[Zr]();this.reserve=i;return HTMLResult.success({name:"div",attributes:{style:s,class:["xfaCaption"]},children:a})}}class Certificate extends StringObject{constructor(e){super(Kn,"certificate");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Certificates extends XFAObject{constructor(e){super(Kn,"certificates",!0);this.credentialServerPolicy=getStringOption(e.credentialServerPolicy,["optional","required"]);this.id=e.id||"";this.url=e.url||"";this.urlPolicy=e.urlPolicy||"";this.use=e.use||"";this.usehref=e.usehref||"";this.encryption=null;this.issuers=null;this.keyUsage=null;this.oids=null;this.signing=null;this.subjectDNs=null}}class CheckButton extends XFAObject{constructor(e){super(Kn,"checkButton",!0);this.id=e.id||"";this.mark=getStringOption(e.mark,["default","check","circle","cross","diamond","square","star"]);this.shape=getStringOption(e.shape,["square","round"]);this.size=getMeasurement(e.size,"10pt");this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[gn](e){const t=toStyle("margin"),i=measureToString(this.size);t.width=t.height=i;let a,s,r;const n=this[Dr]()[Dr](),g=n.items.children.length&&n.items.children[0][gn]().html||[],o={on:(void 0!==g[0]?g[0]:"on").toString(),off:(void 0!==g[1]?g[1]:"off").toString()},c=(n.value?.[rn]()||"off")===o.on||void 0,C=n[wr](),h=n[cn];let l;if(C instanceof ExclGroup){r=C[cn];a="radio";s="xfaRadio";l=C[rr]?.[cn]||C[cn]}else{a="checkbox";s="xfaCheckbox";l=n[rr]?.[cn]||n[cn]}const Q={name:"input",attributes:{class:[s],style:t,fieldId:h,dataId:l,type:a,checked:c,xfaOn:o.on,xfaOff:o.off,"aria-label":ariaLabel(n),"aria-required":!1}};r&&(Q.attributes.name=r);if(isRequired(n)){Q.attributes["aria-required"]=!0;Q.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[Q]})}}class ChoiceList extends XFAObject{constructor(e){super(Kn,"choiceList",!0);this.commitOn=getStringOption(e.commitOn,["select","exit"]);this.id=e.id||"";this.open=getStringOption(e.open,["userControl","always","multiSelect","onEntry"]);this.textEntry=getInteger({data:e.textEntry,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[gn](e){const t=toStyle(this,"border","margin"),i=this[Dr]()[Dr](),a={fontSize:`calc(${i.font?.size||10}px * var(--scale-factor))`},s=[];if(i.items.children.length>0){const e=i.items;let t=0,r=0;if(2===e.children.length){t=e.children[0].save;r=1-t}const n=e.children[t][gn]().html,g=e.children[r][gn]().html;let o=!1;const c=i.value?.[rn]()||"";for(let e=0,t=n.length;e<t;e++){const t={name:"option",attributes:{value:g[e]||n[e],style:a},value:n[e]};g[e]===c&&(t.attributes.selected=o=!0);s.push(t)}o||s.splice(0,0,{name:"option",attributes:{hidden:!0,selected:!0},value:" "})}const r={class:["xfaSelect"],fieldId:i[cn],dataId:i[rr]?.[cn]||i[cn],style:t,"aria-label":ariaLabel(i),"aria-required":!1};if(isRequired(i)){r["aria-required"]=!0;r.required=!0}"multiSelect"===this.open&&(r.multiple=!0);return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[{name:"select",children:s,attributes:r}]})}}class Color extends XFAObject{constructor(e){super(Kn,"color",!0);this.cSpace=getStringOption(e.cSpace,["SRGB"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.value=e.value?function getColor(e,t=[0,0,0]){let[i,a,s]=t;if(!e)return{r:i,g:a,b:s};const r=e.trim().split(/\s*,\s*/).map((e=>Math.min(Math.max(0,parseInt(e.trim(),10)),255))).map((e=>isNaN(e)?0:e));if(r.length<3)return{r:i,g:a,b:s};[i,a,s]=r;return{r:i,g:a,b:s}}(e.value):"";this.extras=null}[Sr](){return!1}[In](){return this.value?Util.makeHexColor(this.value.r,this.value.g,this.value.b):null}}class Comb extends XFAObject{constructor(e){super(Kn,"comb");this.id=e.id||"";this.numberOfCells=getInteger({data:e.numberOfCells,defaultValue:0,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||""}}class Connect extends XFAObject{constructor(e){super(Kn,"connect",!0);this.connection=e.connection||"";this.id=e.id||"";this.ref=e.ref||"";this.usage=getStringOption(e.usage,["exportAndImport","exportOnly","importOnly"]);this.use=e.use||"";this.usehref=e.usehref||"";this.picture=null}}class ContentArea extends XFAObject{constructor(e){super(Kn,"contentArea",!0);this.h=getMeasurement(e.h);this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=getMeasurement(e.w);this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null}[gn](e){const t={left:measureToString(this.x),top:measureToString(this.y),width:measureToString(this.w),height:measureToString(this.h)},i=["xfaContentarea"];isPrintOnly(this)&&i.push("xfaPrintOnly");return HTMLResult.success({name:"div",children:[],attributes:{style:t,class:i,id:this[cn]}})}}class Corner extends XFAObject{constructor(e){super(Kn,"corner",!0);this.id=e.id||"";this.inverted=getInteger({data:e.inverted,defaultValue:0,validate:e=>1===e});this.join=getStringOption(e.join,["square","round"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.radius=getMeasurement(e.radius);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](){const e=toStyle(this,"visibility");e.radius=measureToString("square"===this.join?0:this.radius);return e}}class DateElement extends ContentObject{constructor(e){super(Kn,"date");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=this[sr].trim();this[sr]=e?new Date(e):null}[gn](e){return valueToHtml(this[sr]?this[sr].toString():"")}}class DateTime extends ContentObject{constructor(e){super(Kn,"dateTime");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=this[sr].trim();this[sr]=e?new Date(e):null}[gn](e){return valueToHtml(this[sr]?this[sr].toString():"")}}class DateTimeEdit extends XFAObject{constructor(e){super(Kn,"dateTimeEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.picker=getStringOption(e.picker,["host","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[gn](e){const t=toStyle(this,"border","font","margin"),i=this[Dr]()[Dr](),a={name:"input",attributes:{type:"text",fieldId:i[cn],dataId:i[rr]?.[cn]||i[cn],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Decimal extends ContentObject{constructor(e){super(Kn,"decimal");this.fracDigits=getInteger({data:e.fracDigits,defaultValue:2,validate:e=>!0});this.id=e.id||"";this.leadDigits=getInteger({data:e.leadDigits,defaultValue:-1,validate:e=>!0});this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=parseFloat(this[sr].trim());this[sr]=isNaN(e)?null:e}[gn](e){return valueToHtml(null!==this[sr]?this[sr].toString():"")}}class DefaultUi extends XFAObject{constructor(e){super(Kn,"defaultUi",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class Desc extends XFAObject{constructor(e){super(Kn,"desc",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class DigestMethod extends OptionObject{constructor(e){super(Kn,"digestMethod",["","SHA1","SHA256","SHA512","RIPEMD160"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class DigestMethods extends XFAObject{constructor(e){super(Kn,"digestMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.digestMethod=new XFAObjectArray}}class Draw extends XFAObject{constructor(e){super(Kn,"draw",!0);this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.border=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.value=null;this.setProperty=new XFAObjectArray}[an](e){_setValue(this,e)}[gn](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;fixDimensions(this);this[Vr]();const t=this.w,i=this.h,{w:a,h:s,isBroken:r}=layoutNode(this,e);if(a&&""===this.w){if(r&&this[wr]()[Jr]()){this[Zr]();return HTMLResult.FAILURE}this.w=a}s&&""===this.h&&(this.h=s);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=t;this.h=i;this[Zr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const n=toStyle(this,"font","hAlign","dimensions","position","presence","rotate","anchorType","border","margin");setMinMaxDimensions(this,n);if(n.margin){n.padding=n.margin;delete n.margin}const g=["xfaDraw"];this.font&&g.push("xfaFont");isPrintOnly(this)&&g.push("xfaPrintOnly");const o={style:n,id:this[cn],class:g};this.name&&(o.xfaName=this.name);const c={name:"div",attributes:o,children:[]};applyAssist(this,o);const C=computeBbox(this,c,e),h=this.value?this.value[gn](e).html:null;if(null===h){this.w=t;this.h=i;this[Zr]();return HTMLResult.success(createWrapper(this,c),C)}c.children.push(h);setPara(this,n,h);this.w=t;this.h=i;this[Zr]();return HTMLResult.success(createWrapper(this,c),C)}}class Edge extends XFAObject{constructor(e){super(Kn,"edge",!0);this.cap=getStringOption(e.cap,["square","butt","round"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](){const e=toStyle(this,"visibility");Object.assign(e,{linecap:this.cap,width:measureToString(this.thickness),color:this.color?this.color[In]():"#000000",style:""});if("visible"!==this.presence)e.style="none";else switch(this.stroke){case"solid":e.style="solid";break;case"dashDot":case"dashDotDot":case"dashed":e.style="dashed";break;case"dotted":e.style="dotted";break;case"embossed":e.style="ridge";break;case"etched":e.style="groove";break;case"lowered":e.style="inset";break;case"raised":e.style="outset"}return e}}class Encoding extends OptionObject{constructor(e){super(Kn,"encoding",["adbe.x509.rsa_sha1","adbe.pkcs7.detached","adbe.pkcs7.sha1"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Encodings extends XFAObject{constructor(e){super(Kn,"encodings",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encoding=new XFAObjectArray}}class Encrypt extends XFAObject{constructor(e){super(Kn,"encrypt",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=null}}class EncryptData extends XFAObject{constructor(e){super(Kn,"encryptData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["encrypt","decrypt"]);this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Encryption extends XFAObject{constructor(e){super(Kn,"encryption",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class EncryptionMethod extends OptionObject{constructor(e){super(Kn,"encryptionMethod",["","AES256-CBC","TRIPLEDES-CBC","AES128-CBC","AES192-CBC"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EncryptionMethods extends XFAObject{constructor(e){super(Kn,"encryptionMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encryptionMethod=new XFAObjectArray}}class Event extends XFAObject{constructor(e){super(Kn,"event",!0);this.activity=getStringOption(e.activity,["click","change","docClose","docReady","enter","exit","full","indexChange","initialize","mouseDown","mouseEnter","mouseExit","mouseUp","postExecute","postOpen","postPrint","postSave","postSign","postSubmit","preExecute","preOpen","prePrint","preSave","preSign","preSubmit","ready","validationState"]);this.id=e.id||"";this.listen=getStringOption(e.listen,["refOnly","refAndDescendents"]);this.name=e.name||"";this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.encryptData=null;this.execute=null;this.script=null;this.signData=null;this.submit=null}}class ExData extends ContentObject{constructor(e){super(Kn,"exData");this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.maxLength=getInteger({data:e.maxLength,defaultValue:-1,validate:e=>e>=-1});this.name=e.name||"";this.rid=e.rid||"";this.transferEncoding=getStringOption(e.transferEncoding,["none","base64","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[Gr](){return"text/html"===this.contentType}[Pr](e){if("text/html"===this.contentType&&e[Tr]===hn.xhtml.id){this[sr]=e;return!0}if("text/xml"===this.contentType){this[sr]=e;return!0}return!1}[gn](e){return"text/html"===this.contentType&&this[sr]?this[sr][gn](e):HTMLResult.EMPTY}}class ExObject extends XFAObject{constructor(e){super(Kn,"exObject",!0);this.archive=e.archive||"";this.classId=e.classId||"";this.codeBase=e.codeBase||"";this.codeType=e.codeType||"";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class ExclGroup extends XFAObject{constructor(e){super(Kn,"exclGroup",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.margin=null;this.para=null;this.traversal=null;this.validate=null;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.field=new XFAObjectArray;this.setProperty=new XFAObjectArray}[xr](){return!0}[Sr](){return!0}[an](e){for(const t of this.field.children){if(!t.value){const e=new Value({});t[_s](e);t.value=e}t.value[an](e)}}[Jr](){return this.layout.endsWith("-tb")&&0===this[gr].attempt&&this[gr].numberInLine>0||this[Dr]()[Jr]()}[Hr](){const e=this[wr]();if(!e[Hr]())return!1;if(void 0!==this[gr]._isSplittable)return this[gr]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[gr]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[gr].numberInLine)return!1;this[gr]._isSplittable=!0;return!0}[Ir](){return flushHTML(this)}[zs](e,t){addHTML(this,e,t)}[hr](){return getAvailableSpace(this)}[gn](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[cn],class:[]};setAccess(this,i.class);this[gr]||(this[gr]=Object.create(null));Object.assign(this[gr],{children:t,attributes:i,attempt:0,line:null,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[Hr]();a||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const s=new Set(["field"]);if(this.layout.includes("row")){const e=this[wr]().columnWidths;if(Array.isArray(e)&&e.length>0){this[gr].columnWidths=e;this[gr].currentColumn=0}}const r=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),n=["xfaExclgroup"],g=layoutClass(this);g&&n.push(g);isPrintOnly(this)&&n.push("xfaPrintOnly");i.style=r;i.class=n;this.name&&(i.xfaName=this.name);this[Vr]();const o="lr-tb"===this.layout||"rl-tb"===this.layout,c=o?2:1;for(;this[gr].attempt<c;this[gr].attempt++){o&&1===this[gr].attempt&&(this[gr].numberInLine=0);const e=this[$s]({filter:s,include:!0});if(e.success)break;if(e.isBreak()){this[Zr]();return e}if(o&&0===this[gr].attempt&&0===this[gr].numberInLine&&!this[br]()[gr].noLayoutFailure){this[gr].attempt=c;break}}this[Zr]();a||unsetFirstUnsplittable(this);if(this[gr].attempt===c){a||delete this[gr];return HTMLResult.FAILURE}let C=0,h=0;if(this.margin){C=this.margin.leftInset+this.margin.rightInset;h=this.margin.topInset+this.margin.bottomInset}const l=Math.max(this[gr].width+C,this.w||0),Q=Math.max(this[gr].height+h,this.h||0),E=[this.x,this.y,l,Q];""===this.w&&(r.width=measureToString(l));""===this.h&&(r.height=measureToString(Q));const u={name:"div",attributes:i,children:t};applyAssist(this,i);delete this[gr];return HTMLResult.success(createWrapper(this,u),E)}}class Execute extends XFAObject{constructor(e){super(Kn,"execute");this.connection=e.connection||"";this.executeType=getStringOption(e.executeType,["import","remerge"]);this.id=e.id||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Extras extends XFAObject{constructor(e){super(Kn,"extras",!0);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.extras=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class Field extends XFAObject{constructor(e){super(Kn,"field",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.format=null;this.items=new XFAObjectArray(2);this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.validate=null;this.value=null;this.bindItems=new XFAObjectArray;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.setProperty=new XFAObjectArray}[xr](){return!0}[an](e){_setValue(this,e)}[gn](e){setTabIndex(this);if(!this.ui){this.ui=new Ui({});this.ui[Fr]=this[Fr];this[_s](this.ui);let e;switch(this.items.children.length){case 0:e=new TextEdit({});this.ui.textEdit=e;break;case 1:e=new CheckButton({});this.ui.checkButton=e;break;case 2:e=new ChoiceList({});this.ui.choiceList=e}this.ui[_s](e)}if(!this.ui||"hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;this.caption&&delete this.caption[gr];this[Vr]();const t=this.caption?this.caption[gn](e).html:null,i=this.w,a=this.h;let s=0,r=0;if(this.margin){s=this.margin.leftInset+this.margin.rightInset;r=this.margin.topInset+this.margin.bottomInset}let n=null;if(""===this.w||""===this.h){let t=null,i=null,a=0,g=0;if(this.ui.checkButton)a=g=this.ui.checkButton.size;else{const{w:t,h:i}=layoutNode(this,e);if(null!==t){a=t;g=i}else g=function fonts_getMetrics(e,t=!1){let i=null;if(e){const t=stripQuotes(e.typeface),a=e[Fr].fontFinder.find(t);i=selectFont(e,a)}if(!i)return{lineHeight:12,lineGap:2,lineNoGap:10};const a=e.size||10,s=i.lineHeight?Math.max(t?0:1.2,i.lineHeight):1.2,r=void 0===i.lineGap?.2:i.lineGap;return{lineHeight:s*a,lineGap:r*a,lineNoGap:Math.max(1,s-r)*a}}(this.font,!0).lineNoGap}n=getBorderDims(this.ui[dr]());a+=n.w;g+=n.h;if(this.caption){const{w:s,h:r,isBroken:n}=this.caption[dr](e);if(n&&this[wr]()[Jr]()){this[Zr]();return HTMLResult.FAILURE}t=s;i=r;switch(this.caption.placement){case"left":case"right":case"inline":t+=a;break;case"top":case"bottom":i+=g}}else{t=a;i=g}if(t&&""===this.w){t+=s;this.w=Math.min(this.maxW<=0?1/0:this.maxW,this.minW+1<t?t:this.minW)}if(i&&""===this.h){i+=r;this.h=Math.min(this.maxH<=0?1/0:this.maxH,this.minH+1<i?i:this.minH)}}this[Zr]();fixDimensions(this);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=i;this.h=a;this[Zr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const g=toStyle(this,"font","dimensions","position","rotate","anchorType","presence","margin","hAlign");setMinMaxDimensions(this,g);const o=["xfaField"];this.font&&o.push("xfaFont");isPrintOnly(this)&&o.push("xfaPrintOnly");const c={style:g,id:this[cn],class:o};if(g.margin){g.padding=g.margin;delete g.margin}setAccess(this,o);this.name&&(c.xfaName=this.name);const C=[],h={name:"div",attributes:c,children:C};applyAssist(this,c);const l=this.border?this.border[In]():null,Q=computeBbox(this,h,e),E=this.ui[gn]().html;if(!E){Object.assign(g,l);return HTMLResult.success(createWrapper(this,h),Q)}this[sn]&&(E.children?.[0]?E.children[0].attributes.tabindex=this[sn]:E.attributes.tabindex=this[sn]);E.attributes.style||(E.attributes.style=Object.create(null));let u=null;if(this.ui.button){1===E.children.length&&([u]=E.children.splice(0,1));Object.assign(E.attributes.style,l)}else Object.assign(g,l);C.push(E);if(this.value)if(this.ui.imageEdit)E.children.push(this.value[gn]().html);else if(!this.ui.button){let e="";if(this.value.exData)e=this.value.exData[rn]();else if(this.value.text)e=this.value.text[dr]();else{const t=this.value[gn]().html;null!==t&&(e=t.children[0].value)}this.ui.textEdit&&this.value.text?.maxChars&&(E.children[0].attributes.maxLength=this.value.text.maxChars);if(e){if(this.ui.numericEdit){e=parseFloat(e);e=isNaN(e)?"":e.toString()}"textarea"===E.children[0].name?E.children[0].attributes.textContent=e:E.children[0].attributes.value=e}}if(!this.ui.imageEdit&&E.children?.[0]&&this.h){n=n||getBorderDims(this.ui[dr]());let t=0;if(this.caption&&["top","bottom"].includes(this.caption.placement)){t=this.caption.reserve;t<=0&&(t=this.caption[dr](e).h);const i=this.h-t-r-n.h;E.children[0].attributes.style.height=measureToString(i)}else E.children[0].attributes.style.height="100%"}u&&E.children.push(u);if(!t){E.attributes.class&&E.attributes.class.push("xfaLeft");this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}if(this.ui.button){g.padding&&delete g.padding;"div"===t.name&&(t.name="span");E.children.push(t);return HTMLResult.success(h,Q)}this.ui.checkButton&&(t.attributes.class[0]="xfaCaptionForCheckButton");E.attributes.class||(E.attributes.class=[]);E.children.splice(0,0,t);switch(this.caption.placement){case"left":case"inline":E.attributes.class.push("xfaLeft");break;case"right":E.attributes.class.push("xfaRight");break;case"top":E.attributes.class.push("xfaTop");break;case"bottom":E.attributes.class.push("xfaBottom")}this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}}class Fill extends XFAObject{constructor(e){super(Kn,"fill",!0);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null;this.linear=null;this.pattern=null;this.radial=null;this.solid=null;this.stipple=null}[In](){const e=this[Dr](),t=e[Dr]()[Dr](),i=Object.create(null);let a="color",s=a;if(e instanceof Border){a="background-color";s="background";t instanceof Ui&&(i.backgroundColor="white")}if(e instanceof Rectangle||e instanceof Arc){a=s="fill";i.fill="white"}for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"color"===e)continue;const t=this[e];if(!(t instanceof XFAObject))continue;const r=t[In](this.color);r&&(i[r.startsWith("#")?a:s]=r);return i}if(this.color?.value){const e=this.color[In]();i[e.startsWith("#")?a:s]=e}return i}}class Filter extends XFAObject{constructor(e){super(Kn,"filter",!0);this.addRevocationInfo=getStringOption(e.addRevocationInfo,["","required","optional","none"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.version=getInteger({data:this.version,defaultValue:5,validate:e=>e>=1&&e<=5});this.appearanceFilter=null;this.certificates=null;this.digestMethods=null;this.encodings=null;this.encryptionMethods=null;this.handler=null;this.lockDocument=null;this.mdp=null;this.reasons=null;this.timeStamp=null}}class Float extends ContentObject{constructor(e){super(Kn,"float");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=parseFloat(this[sr].trim());this[sr]=isNaN(e)?null:e}[gn](e){return valueToHtml(null!==this[sr]?this[sr].toString():"")}}class template_Font extends XFAObject{constructor(e){super(Kn,"font",!0);this.baselineShift=getMeasurement(e.baselineShift);this.fontHorizontalScale=getFloat({data:e.fontHorizontalScale,defaultValue:100,validate:e=>e>=0});this.fontVerticalScale=getFloat({data:e.fontVerticalScale,defaultValue:100,validate:e=>e>=0});this.id=e.id||"";this.kerningMode=getStringOption(e.kerningMode,["none","pair"]);this.letterSpacing=getMeasurement(e.letterSpacing,"0");this.lineThrough=getInteger({data:e.lineThrough,defaultValue:0,validate:e=>1===e||2===e});this.lineThroughPeriod=getStringOption(e.lineThroughPeriod,["all","word"]);this.overline=getInteger({data:e.overline,defaultValue:0,validate:e=>1===e||2===e});this.overlinePeriod=getStringOption(e.overlinePeriod,["all","word"]);this.posture=getStringOption(e.posture,["normal","italic"]);this.size=getMeasurement(e.size,"10pt");this.typeface=e.typeface||"Courier";this.underline=getInteger({data:e.underline,defaultValue:0,validate:e=>1===e||2===e});this.underlinePeriod=getStringOption(e.underlinePeriod,["all","word"]);this.use=e.use||"";this.usehref=e.usehref||"";this.weight=getStringOption(e.weight,["normal","bold"]);this.extras=null;this.fill=null}[Ar](e){super[Ar](e);this[Fr].usedTypefaces.add(this.typeface)}[In](){const e=toStyle(this,"fill"),t=e.color;if(t)if("#000000"===t)delete e.color;else if(!t.startsWith("#")){e.background=t;e.backgroundClip="text";e.color="transparent"}this.baselineShift&&(e.verticalAlign=measureToString(this.baselineShift));e.fontKerning="none"===this.kerningMode?"none":"normal";e.letterSpacing=measureToString(this.letterSpacing);if(0!==this.lineThrough){e.textDecoration="line-through";2===this.lineThrough&&(e.textDecorationStyle="double")}if(0!==this.overline){e.textDecoration="overline";2===this.overline&&(e.textDecorationStyle="double")}e.fontStyle=this.posture;e.fontSize=measureToString(.99*this.size);setFontFamily(this,this,this[Fr].fontFinder,e);if(0!==this.underline){e.textDecoration="underline";2===this.underline&&(e.textDecorationStyle="double")}e.fontWeight=this.weight;return e}}class Format extends XFAObject{constructor(e){super(Kn,"format",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null}}class Handler extends StringObject{constructor(e){super(Kn,"handler");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Hyphenation extends XFAObject{constructor(e){super(Kn,"hyphenation");this.excludeAllCaps=getInteger({data:e.excludeAllCaps,defaultValue:0,validate:e=>1===e});this.excludeInitialCap=getInteger({data:e.excludeInitialCap,defaultValue:0,validate:e=>1===e});this.hyphenate=getInteger({data:e.hyphenate,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.pushCharacterCount=getInteger({data:e.pushCharacterCount,defaultValue:3,validate:e=>e>=0});this.remainCharacterCount=getInteger({data:e.remainCharacterCount,defaultValue:3,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||"";this.wordCharacterCount=getInteger({data:e.wordCharacterCount,defaultValue:7,validate:e=>e>=0})}}class Image extends StringObject{constructor(e){super(Kn,"image");this.aspect=getStringOption(e.aspect,["fit","actual","height","none","width"]);this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.name=e.name||"";this.transferEncoding=getStringOption(e.transferEncoding,["base64","none","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[gn](){if(this.contentType&&!On.has(this.contentType.toLowerCase()))return HTMLResult.EMPTY;let e=this[Fr].images&&this[Fr].images.get(this.href);if(!e&&(this.href||!this[sr]))return HTMLResult.EMPTY;e||"base64"!==this.transferEncoding||(e=stringToBytes(atob(this[sr])));if(!e)return HTMLResult.EMPTY;if(!this.contentType){for(const[t,i]of Pn)if(e.length>t.length&&t.every(((t,i)=>t===e[i]))){this.contentType=i;break}if(!this.contentType)return HTMLResult.EMPTY}const t=new Blob([e],{type:this.contentType});let i;switch(this.aspect){case"fit":case"actual":break;case"height":i={height:"100%",objectFit:"fill"};break;case"none":i={width:"100%",height:"100%",objectFit:"fill"};break;case"width":i={width:"100%",objectFit:"fill"}}const a=this[Dr]();return HTMLResult.success({name:"img",attributes:{class:["xfaImage"],style:i,src:URL.createObjectURL(t),alt:a?ariaLabel(a[Dr]()):null}})}}class ImageEdit extends XFAObject{constructor(e){super(Kn,"imageEdit",!0);this.data=getStringOption(e.data,["link","embed"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[gn](e){return"embed"===this.data?HTMLResult.success({name:"div",children:[],attributes:{}}):HTMLResult.EMPTY}}class Integer extends ContentObject{constructor(e){super(Kn,"integer");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=parseInt(this[sr].trim(),10);this[sr]=isNaN(e)?null:e}[gn](e){return valueToHtml(null!==this[sr]?this[sr].toString():"")}}class Issuers extends XFAObject{constructor(e){super(Kn,"issuers",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Items extends XFAObject{constructor(e){super(Kn,"items",!0);this.id=e.id||"";this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.ref=e.ref||"";this.save=getInteger({data:e.save,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[gn](){const e=[];for(const t of this[pr]())e.push(t[rn]());return HTMLResult.success(e)}}class Keep extends XFAObject{constructor(e){super(Kn,"keep",!0);this.id=e.id||"";const t=["none","contentArea","pageArea"];this.intact=getStringOption(e.intact,t);this.next=getStringOption(e.next,t);this.previous=getStringOption(e.previous,t);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class KeyUsage extends XFAObject{constructor(e){super(Kn,"keyUsage");const t=["","yes","no"];this.crlSign=getStringOption(e.crlSign,t);this.dataEncipherment=getStringOption(e.dataEncipherment,t);this.decipherOnly=getStringOption(e.decipherOnly,t);this.digitalSignature=getStringOption(e.digitalSignature,t);this.encipherOnly=getStringOption(e.encipherOnly,t);this.id=e.id||"";this.keyAgreement=getStringOption(e.keyAgreement,t);this.keyCertSign=getStringOption(e.keyCertSign,t);this.keyEncipherment=getStringOption(e.keyEncipherment,t);this.nonRepudiation=getStringOption(e.nonRepudiation,t);this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Line extends XFAObject{constructor(e){super(Kn,"line",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.slope=getStringOption(e.slope,["\\","/"]);this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null}[gn](){const e=this[Dr]()[Dr](),t=this.edge||new Edge({}),i=t[In](),a=Object.create(null),s="visible"===t.presence?t.thickness:0;a.strokeWidth=measureToString(s);a.stroke=i.color;let r,n,g,o,c="100%",C="100%";if(e.w<=s){[r,n,g,o]=["50%",0,"50%","100%"];c=a.strokeWidth}else if(e.h<=s){[r,n,g,o]=[0,"50%","100%","50%"];C=a.strokeWidth}else"\\"===this.slope?[r,n,g,o]=[0,0,"100%","100%"]:[r,n,g,o]=[0,"100%","100%",0];const h={name:"svg",children:[{name:"line",attributes:{xmlns:Tn,x1:r,y1:n,x2:g,y2:o,style:a}}],attributes:{xmlns:Tn,width:c,height:C,style:{overflow:"visible"}}};if(hasMargin(e))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[h]});h.attributes.style.position="absolute";return HTMLResult.success(h)}}class Linear extends XFAObject{constructor(e){super(Kn,"linear",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toRight","toBottom","toLeft","toTop"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](e){e=e?e[In]():"#FFFFFF";return`linear-gradient(${this.type.replace(/([RBLT])/," $1").toLowerCase()}, ${e}, ${this.color?this.color[In]():"#000000"})`}}class LockDocument extends ContentObject{constructor(e){super(Kn,"lockDocument");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}[or](){this[sr]=getStringOption(this[sr],["auto","0","1"])}}class Manifest extends XFAObject{constructor(e){super(Kn,"manifest",!0);this.action=getStringOption(e.action,["include","all","exclude"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.ref=new XFAObjectArray}}class Margin extends XFAObject{constructor(e){super(Kn,"margin",!0);this.bottomInset=getMeasurement(e.bottomInset,"0");this.id=e.id||"";this.leftInset=getMeasurement(e.leftInset,"0");this.rightInset=getMeasurement(e.rightInset,"0");this.topInset=getMeasurement(e.topInset,"0");this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[In](){return{margin:measureToString(this.topInset)+" "+measureToString(this.rightInset)+" "+measureToString(this.bottomInset)+" "+measureToString(this.leftInset)}}}class Mdp extends XFAObject{constructor(e){super(Kn,"mdp");this.id=e.id||"";this.permissions=getInteger({data:e.permissions,defaultValue:2,validate:e=>1===e||3===e});this.signatureType=getStringOption(e.signatureType,["filler","author"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Medium extends XFAObject{constructor(e){super(Kn,"medium");this.id=e.id||"";this.imagingBBox=function getBBox(e){const t=-1;if(!e)return{x:t,y:t,width:t,height:t};const i=e.trim().split(/\s*,\s*/).map((e=>getMeasurement(e,"-1")));if(i.length<4||i[2]<0||i[3]<0)return{x:t,y:t,width:t,height:t};const[a,s,r,n]=i;return{x:a,y:s,width:r,height:n}}(e.imagingBBox);this.long=getMeasurement(e.long);this.orientation=getStringOption(e.orientation,["portrait","landscape"]);this.short=getMeasurement(e.short);this.stock=e.stock||"";this.trayIn=getStringOption(e.trayIn,["auto","delegate","pageFront"]);this.trayOut=getStringOption(e.trayOut,["auto","delegate"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Message extends XFAObject{constructor(e){super(Kn,"message",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.text=new XFAObjectArray}}class NumericEdit extends XFAObject{constructor(e){super(Kn,"numericEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[gn](e){const t=toStyle(this,"border","font","margin"),i=this[Dr]()[Dr](),a={name:"input",attributes:{type:"text",fieldId:i[cn],dataId:i[rr]?.[cn]||i[cn],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Occur extends XFAObject{constructor(e){super(Kn,"occur",!0);this.id=e.id||"";this.initial=""!==e.initial?getInteger({data:e.initial,defaultValue:"",validate:e=>!0}):"";this.max=""!==e.max?getInteger({data:e.max,defaultValue:1,validate:e=>!0}):"";this.min=""!==e.min?getInteger({data:e.min,defaultValue:1,validate:e=>!0}):"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Ar](){const e=this[Dr](),t=this.min;""===this.min&&(this.min=e instanceof PageArea||e instanceof PageSet?0:1);""===this.max&&(this.max=""===t?e instanceof PageArea||e instanceof PageSet?-1:1:this.min);-1!==this.max&&this.max<this.min&&(this.max=this.min);""===this.initial&&(this.initial=e instanceof Template?1:this.min)}}class Oid extends StringObject{constructor(e){super(Kn,"oid");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Oids extends XFAObject{constructor(e){super(Kn,"oids",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.oid=new XFAObjectArray}}class Overflow extends XFAObject{constructor(e){super(Kn,"overflow");this.id=e.id||"";this.leader=e.leader||"";this.target=e.target||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}[dr](){if(!this[gr]){const e=this[Dr](),t=this[br](),i=t[An](this.target,e),a=t[An](this.leader,e),s=t[An](this.trailer,e);this[gr]={target:i?.[0]||null,leader:a?.[0]||null,trailer:s?.[0]||null,addLeader:!1,addTrailer:!1}}return this[gr]}}class PageArea extends XFAObject{constructor(e){super(Kn,"pageArea",!0);this.blankOrNotBlank=getStringOption(e.blankOrNotBlank,["any","blank","notBlank"]);this.id=e.id||"";this.initialNumber=getInteger({data:e.initialNumber,defaultValue:1,validate:e=>!0});this.name=e.name||"";this.numbered=getInteger({data:e.numbered,defaultValue:1,validate:e=>!0});this.oddOrEven=getStringOption(e.oddOrEven,["any","even","odd"]);this.pagePosition=getStringOption(e.pagePosition,["any","first","last","only","rest"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.desc=null;this.extras=null;this.medium=null;this.occur=null;this.area=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.draw=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray}[vr](){if(!this[gr]){this[gr]={numberOfUse:0};return!0}return!this.occur||-1===this.occur.max||this[gr].numberOfUse<this.occur.max}[er](){delete this[gr]}[yr](){this[gr]||(this[gr]={numberOfUse:0});const e=this[Dr]();if("orderedOccurrence"===e.relation&&this[vr]()){this[gr].numberOfUse+=1;return this}return e[yr]()}[hr](){return this[gr].space||{width:0,height:0}}[gn](){this[gr]||(this[gr]={numberOfUse:1});const e=[];this[gr].children=e;const t=Object.create(null);if(this.medium&&this.medium.short&&this.medium.long){t.width=measureToString(this.medium.short);t.height=measureToString(this.medium.long);this[gr].space={width:this.medium.short,height:this.medium.long};if("landscape"===this.medium.orientation){const e=t.width;t.width=t.height;t.height=e;this[gr].space={width:this.medium.long,height:this.medium.short}}}else warn("XFA - No medium specified in pageArea: please file a bug.");this[$s]({filter:new Set(["area","draw","field","subform"]),include:!0});this[$s]({filter:new Set(["contentArea"]),include:!0});return HTMLResult.success({name:"div",children:e,attributes:{class:["xfaPage"],id:this[cn],style:t,xfaName:this.name}})}}class PageSet extends XFAObject{constructor(e){super(Kn,"pageSet",!0);this.duplexImposition=getStringOption(e.duplexImposition,["longEdge","shortEdge"]);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["orderedOccurrence","duplexPaginated","simplexPaginated"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.occur=null;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray}[er](){for(const e of this.pageArea.children)e[er]();for(const e of this.pageSet.children)e[er]()}[vr](){return!this.occur||-1===this.occur.max||this[gr].numberOfUse<this.occur.max}[yr](){this[gr]||(this[gr]={numberOfUse:1,pageIndex:-1,pageSetIndex:-1});if("orderedOccurrence"===this.relation){if(this[gr].pageIndex+1<this.pageArea.children.length){this[gr].pageIndex+=1;return this.pageArea.children[this[gr].pageIndex][yr]()}if(this[gr].pageSetIndex+1<this.pageSet.children.length){this[gr].pageSetIndex+=1;return this.pageSet.children[this[gr].pageSetIndex][yr]()}if(this[vr]()){this[gr].numberOfUse+=1;this[gr].pageIndex=-1;this[gr].pageSetIndex=-1;return this[yr]()}const e=this[Dr]();if(e instanceof PageSet)return e[yr]();this[er]();return this[yr]()}const e=this[br]()[gr].pageNumber,t=e%2==0?"even":"odd",i=0===e?"first":"rest";let a=this.pageArea.children.find((e=>e.oddOrEven===t&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&"any"===e.pagePosition));return a||this.pageArea.children[0]}}class Para extends XFAObject{constructor(e){super(Kn,"para",!0);this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.lineHeight=e.lineHeight?getMeasurement(e.lineHeight,"0pt"):"";this.marginLeft=e.marginLeft?getMeasurement(e.marginLeft,"0pt"):"";this.marginRight=e.marginRight?getMeasurement(e.marginRight,"0pt"):"";this.orphans=getInteger({data:e.orphans,defaultValue:0,validate:e=>e>=0});this.preserve=e.preserve||"";this.radixOffset=e.radixOffset?getMeasurement(e.radixOffset,"0pt"):"";this.spaceAbove=e.spaceAbove?getMeasurement(e.spaceAbove,"0pt"):"";this.spaceBelow=e.spaceBelow?getMeasurement(e.spaceBelow,"0pt"):"";this.tabDefault=e.tabDefault?getMeasurement(this.tabDefault):"";this.tabStops=(e.tabStops||"").trim().split(/\s+/).map(((e,t)=>t%2==1?getMeasurement(e):e));this.textIndent=e.textIndent?getMeasurement(e.textIndent,"0pt"):"";this.use=e.use||"";this.usehref=e.usehref||"";this.vAlign=getStringOption(e.vAlign,["top","bottom","middle"]);this.widows=getInteger({data:e.widows,defaultValue:0,validate:e=>e>=0});this.hyphenation=null}[In](){const e=toStyle(this,"hAlign");""!==this.marginLeft&&(e.paddingLeft=measureToString(this.marginLeft));""!==this.marginRight&&(e.paddingight=measureToString(this.marginRight));""!==this.spaceAbove&&(e.paddingTop=measureToString(this.spaceAbove));""!==this.spaceBelow&&(e.paddingBottom=measureToString(this.spaceBelow));if(""!==this.textIndent){e.textIndent=measureToString(this.textIndent);fixTextIndent(e)}this.lineHeight>0&&(e.lineHeight=measureToString(this.lineHeight));""!==this.tabDefault&&(e.tabSize=measureToString(this.tabDefault));this.tabStops.length;this.hyphenatation&&Object.assign(e,this.hyphenatation[In]());return e}}class PasswordEdit extends XFAObject{constructor(e){super(Kn,"passwordEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.passwordChar=e.passwordChar||"*";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}}class template_Pattern extends XFAObject{constructor(e){super(Kn,"pattern",!0);this.id=e.id||"";this.type=getStringOption(e.type,["crossHatch","crossDiagonal","diagonalLeft","diagonalRight","horizontal","vertical"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](e){e=e?e[In]():"#FFFFFF";const t=this.color?this.color[In]():"#000000",i="repeating-linear-gradient",a=`${e},${e} 5px,${t} 5px,${t} 10px`;switch(this.type){case"crossHatch":return`${i}(to top,${a}) ${i}(to right,${a})`;case"crossDiagonal":return`${i}(45deg,${a}) ${i}(-45deg,${a})`;case"diagonalLeft":return`${i}(45deg,${a})`;case"diagonalRight":return`${i}(-45deg,${a})`;case"horizontal":return`${i}(to top,${a})`;case"vertical":return`${i}(to right,${a})`}return""}}class Picture extends StringObject{constructor(e){super(Kn,"picture");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Proto extends XFAObject{constructor(e){super(Kn,"proto",!0);this.appearanceFilter=new XFAObjectArray;this.arc=new XFAObjectArray;this.area=new XFAObjectArray;this.assist=new XFAObjectArray;this.barcode=new XFAObjectArray;this.bindItems=new XFAObjectArray;this.bookend=new XFAObjectArray;this.boolean=new XFAObjectArray;this.border=new XFAObjectArray;this.break=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.button=new XFAObjectArray;this.calculate=new XFAObjectArray;this.caption=new XFAObjectArray;this.certificate=new XFAObjectArray;this.certificates=new XFAObjectArray;this.checkButton=new XFAObjectArray;this.choiceList=new XFAObjectArray;this.color=new XFAObjectArray;this.comb=new XFAObjectArray;this.connect=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.corner=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.dateTimeEdit=new XFAObjectArray;this.decimal=new XFAObjectArray;this.defaultUi=new XFAObjectArray;this.desc=new XFAObjectArray;this.digestMethod=new XFAObjectArray;this.digestMethods=new XFAObjectArray;this.draw=new XFAObjectArray;this.edge=new XFAObjectArray;this.encoding=new XFAObjectArray;this.encodings=new XFAObjectArray;this.encrypt=new XFAObjectArray;this.encryptData=new XFAObjectArray;this.encryption=new XFAObjectArray;this.encryptionMethod=new XFAObjectArray;this.encryptionMethods=new XFAObjectArray;this.event=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.execute=new XFAObjectArray;this.extras=new XFAObjectArray;this.field=new XFAObjectArray;this.fill=new XFAObjectArray;this.filter=new XFAObjectArray;this.float=new XFAObjectArray;this.font=new XFAObjectArray;this.format=new XFAObjectArray;this.handler=new XFAObjectArray;this.hyphenation=new XFAObjectArray;this.image=new XFAObjectArray;this.imageEdit=new XFAObjectArray;this.integer=new XFAObjectArray;this.issuers=new XFAObjectArray;this.items=new XFAObjectArray;this.keep=new XFAObjectArray;this.keyUsage=new XFAObjectArray;this.line=new XFAObjectArray;this.linear=new XFAObjectArray;this.lockDocument=new XFAObjectArray;this.manifest=new XFAObjectArray;this.margin=new XFAObjectArray;this.mdp=new XFAObjectArray;this.medium=new XFAObjectArray;this.message=new XFAObjectArray;this.numericEdit=new XFAObjectArray;this.occur=new XFAObjectArray;this.oid=new XFAObjectArray;this.oids=new XFAObjectArray;this.overflow=new XFAObjectArray;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray;this.para=new XFAObjectArray;this.passwordEdit=new XFAObjectArray;this.pattern=new XFAObjectArray;this.picture=new XFAObjectArray;this.radial=new XFAObjectArray;this.reason=new XFAObjectArray;this.reasons=new XFAObjectArray;this.rectangle=new XFAObjectArray;this.ref=new XFAObjectArray;this.script=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.signData=new XFAObjectArray;this.signature=new XFAObjectArray;this.signing=new XFAObjectArray;this.solid=new XFAObjectArray;this.speak=new XFAObjectArray;this.stipple=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray;this.subjectDN=new XFAObjectArray;this.subjectDNs=new XFAObjectArray;this.submit=new XFAObjectArray;this.text=new XFAObjectArray;this.textEdit=new XFAObjectArray;this.time=new XFAObjectArray;this.timeStamp=new XFAObjectArray;this.toolTip=new XFAObjectArray;this.traversal=new XFAObjectArray;this.traverse=new XFAObjectArray;this.ui=new XFAObjectArray;this.validate=new XFAObjectArray;this.value=new XFAObjectArray;this.variables=new XFAObjectArray}}class Radial extends XFAObject{constructor(e){super(Kn,"radial",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toEdge","toCenter"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](e){e=e?e[In]():"#FFFFFF";const t=this.color?this.color[In]():"#000000";return`radial-gradient(circle at center, ${"toEdge"===this.type?`${e},${t}`:`${t},${e}`})`}}class Reason extends StringObject{constructor(e){super(Kn,"reason");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Reasons extends XFAObject{constructor(e){super(Kn,"reasons",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.reason=new XFAObjectArray}}class Rectangle extends XFAObject{constructor(e){super(Kn,"rectangle",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.fill=null}[gn](){const e=this.edge.children.length?this.edge.children[0]:new Edge({}),t=e[In](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[In]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;const a=(this.corner.children.length?this.corner.children[0]:new Corner({}))[In](),s={name:"svg",children:[{name:"rect",attributes:{xmlns:Tn,width:"100%",height:"100%",x:0,y:0,rx:a.radius,ry:a.radius,style:i}}],attributes:{xmlns:Tn,style:{overflow:"visible"},width:"100%",height:"100%"}};if(hasMargin(this[Dr]()[Dr]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[s]});s.attributes.style.position="absolute";return HTMLResult.success(s)}}class RefElement extends StringObject{constructor(e){super(Kn,"ref");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Script extends StringObject{constructor(e){super(Kn,"script");this.binding=e.binding||"";this.contentType=e.contentType||"";this.id=e.id||"";this.name=e.name||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class SetProperty extends XFAObject{constructor(e){super(Kn,"setProperty");this.connection=e.connection||"";this.ref=e.ref||"";this.target=e.target||""}}class SignData extends XFAObject{constructor(e){super(Kn,"signData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["sign","clear","verify"]);this.ref=e.ref||"";this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Signature extends XFAObject{constructor(e){super(Kn,"signature",!0);this.id=e.id||"";this.type=getStringOption(e.type,["PDF1.3","PDF1.6"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.filter=null;this.manifest=null;this.margin=null}}class Signing extends XFAObject{constructor(e){super(Kn,"signing",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Solid extends XFAObject{constructor(e){super(Kn,"solid",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[In](e){return e?e[In]():"#FFFFFF"}}class Speak extends StringObject{constructor(e){super(Kn,"speak");this.disable=getInteger({data:e.disable,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.priority=getStringOption(e.priority,["custom","caption","name","toolTip"]);this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Stipple extends XFAObject{constructor(e){super(Kn,"stipple",!0);this.id=e.id||"";this.rate=getInteger({data:e.rate,defaultValue:50,validate:e=>e>=0&&e<=100});this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[In](e){const t=this.rate/100;return Util.makeHexColor(Math.round(e.value.r*(1-t)+this.value.r*t),Math.round(e.value.g*(1-t)+this.value.g*t),Math.round(e.value.b*(1-t)+this.value.b*t))}}class Subform extends XFAObject{constructor(e){super(Kn,"subform",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.allowMacro=getInteger({data:e.allowMacro,defaultValue:0,validate:e=>1===e});this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.columnWidths=(e.columnWidths||"").trim().split(/\s+/).map((e=>"-1"===e?-1:getMeasurement(e)));this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.mergeMode=getStringOption(e.mergeMode,["consumeData","matchTemplate"]);this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.restoreState=getStringOption(e.restoreState,["manual","auto"]);this.scope=getStringOption(e.scope,["name","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.bookend=null;this.border=null;this.break=null;this.calculate=null;this.desc=null;this.extras=null;this.keep=null;this.margin=null;this.occur=null;this.overflow=null;this.pageSet=null;this.para=null;this.traversal=null;this.validate=null;this.variables=null;this.area=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.connect=new XFAObjectArray;this.draw=new XFAObjectArray;this.event=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.proto=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}[wr](){const e=this[Dr]();return e instanceof SubformSet?e[wr]():e}[xr](){return!0}[Jr](){return this.layout.endsWith("-tb")&&0===this[gr].attempt&&this[gr].numberInLine>0||this[Dr]()[Jr]()}*[mr](){yield*getContainedChildren(this)}[Ir](){return flushHTML(this)}[zs](e,t){addHTML(this,e,t)}[hr](){return getAvailableSpace(this)}[Hr](){const e=this[wr]();if(!e[Hr]())return!1;if(void 0!==this[gr]._isSplittable)return this[gr]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[gr]._isSplittable=!1;return!1}if(this.keep&&"none"!==this.keep.intact){this[gr]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[gr].numberInLine)return!1;this[gr]._isSplittable=!0;return!0}[gn](e){setTabIndex(this);if(this.break){if("auto"!==this.break.after||""!==this.break.afterTarget){const e=new BreakAfter({targetType:this.break.after,target:this.break.afterTarget,startNew:this.break.startNew.toString()});e[Fr]=this[Fr];this[_s](e);this.breakAfter.push(e)}if("auto"!==this.break.before||""!==this.break.beforeTarget){const e=new BreakBefore({targetType:this.break.before,target:this.break.beforeTarget,startNew:this.break.startNew.toString()});e[Fr]=this[Fr];this[_s](e);this.breakBefore.push(e)}if(""!==this.break.overflowTarget){const e=new Overflow({target:this.break.overflowTarget,leader:this.break.overflowLeader,trailer:this.break.overflowTrailer});e[Fr]=this[Fr];this[_s](e);this.overflow.push(e)}this[zr](this.break);this.break=null}if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;(this.breakBefore.children.length>1||this.breakAfter.children.length>1)&&warn("XFA - Several breakBefore or breakAfter in subforms: please file a bug.");if(this.breakBefore.children.length>=1){const e=this.breakBefore.children[0];if(handleBreak(e))return HTMLResult.breakNode(e)}if(this[gr]?.afterBreakAfter)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[cn],class:[]};setAccess(this,i.class);this[gr]||(this[gr]=Object.create(null));Object.assign(this[gr],{children:t,line:null,attributes:i,attempt:0,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[br](),s=a[gr].noLayoutFailure,r=this[Hr]();r||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const n=new Set(["area","draw","exclGroup","field","subform","subformSet"]);if(this.layout.includes("row")){const e=this[wr]().columnWidths;if(Array.isArray(e)&&e.length>0){this[gr].columnWidths=e;this[gr].currentColumn=0}}const g=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),o=["xfaSubform"],c=layoutClass(this);c&&o.push(c);i.style=g;i.class=o;this.name&&(i.xfaName=this.name);if(this.overflow){const t=this.overflow[dr]();if(t.addLeader){t.addLeader=!1;handleOverflow(this,t.leader,e)}}this[Vr]();const C="lr-tb"===this.layout||"rl-tb"===this.layout,h=C?2:1;for(;this[gr].attempt<h;this[gr].attempt++){C&&1===this[gr].attempt&&(this[gr].numberInLine=0);const e=this[$s]({filter:n,include:!0});if(e.success)break;if(e.isBreak()){this[Zr]();return e}if(C&&0===this[gr].attempt&&0===this[gr].numberInLine&&!a[gr].noLayoutFailure){this[gr].attempt=h;break}}this[Zr]();r||unsetFirstUnsplittable(this);a[gr].noLayoutFailure=s;if(this[gr].attempt===h){this.overflow&&(this[br]()[gr].overflowNode=this.overflow);r||delete this[gr];return HTMLResult.FAILURE}if(this.overflow){const t=this.overflow[dr]();if(t.addTrailer){t.addTrailer=!1;handleOverflow(this,t.trailer,e)}}let l=0,Q=0;if(this.margin){l=this.margin.leftInset+this.margin.rightInset;Q=this.margin.topInset+this.margin.bottomInset}const E=Math.max(this[gr].width+l,this.w||0),u=Math.max(this[gr].height+Q,this.h||0),d=[this.x,this.y,E,u];""===this.w&&(g.width=measureToString(E));""===this.h&&(g.height=measureToString(u));if(("0px"===g.width||"0px"===g.height)&&0===t.length)return HTMLResult.EMPTY;const f={name:"div",attributes:i,children:t};applyAssist(this,i);const p=HTMLResult.success(createWrapper(this,f),d);if(this.breakAfter.children.length>=1){const e=this.breakAfter.children[0];if(handleBreak(e)){this[gr].afterBreakAfter=p;return HTMLResult.breakNode(e)}}delete this[gr];return p}}class SubformSet extends XFAObject{constructor(e){super(Kn,"subformSet",!0);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["ordered","choice","unordered"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.bookend=null;this.break=null;this.desc=null;this.extras=null;this.occur=null;this.overflow=null;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[mr](){yield*getContainedChildren(this)}[wr](){let e=this[Dr]();for(;!(e instanceof Subform);)e=e[Dr]();return e}[xr](){return!0}}class SubjectDN extends ContentObject{constructor(e){super(Kn,"subjectDN");this.delimiter=e.delimiter||",";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){this[sr]=new Map(this[sr].split(this.delimiter).map((e=>{(e=e.split("=",2))[0]=e[0].trim();return e})))}}class SubjectDNs extends XFAObject{constructor(e){super(Kn,"subjectDNs",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.subjectDN=new XFAObjectArray}}class Submit extends XFAObject{constructor(e){super(Kn,"submit",!0);this.embedPDF=getInteger({data:e.embedPDF,defaultValue:0,validate:e=>1===e});this.format=getStringOption(e.format,["xdp","formdata","pdf","urlencoded","xfd","xml"]);this.id=e.id||"";this.target=e.target||"";this.textEncoding=getKeyword({data:e.textEncoding?e.textEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.use=e.use||"";this.usehref=e.usehref||"";this.xdpContent=e.xdpContent||"";this.encrypt=null;this.encryptData=new XFAObjectArray;this.signData=new XFAObjectArray}}class Template extends XFAObject{constructor(e){super(Kn,"template",!0);this.baseProfile=getStringOption(e.baseProfile,["full","interactiveForms"]);this.extras=null;this.subform=new XFAObjectArray}[or](){0===this.subform.children.length&&warn("XFA - No subforms in template node.");this.subform.children.length>=2&&warn("XFA - Several subforms in template node: please file a bug.");this[sn]=5e3}[Hr](){return!0}[An](e,t){return e.startsWith("#")?[this[kr].get(e.slice(1))]:searchNode(this,t,e,!0,!0)}*[nn](){if(!this.subform.children.length)return HTMLResult.success({name:"div",children:[]});this[gr]={overflowNode:null,firstUnsplittable:null,currentContentArea:null,currentPageArea:null,noLayoutFailure:!1,pageNumber:1,pagePosition:"first",oddOrEven:"odd",blankOrNotBlank:"nonBlank",paraStack:[]};const e=this.subform.children[0];e.pageSet[er]();const t=e.pageSet.pageArea.children,i={name:"div",children:[]};let a=null,s=null,r=null;if(e.breakBefore.children.length>=1){s=e.breakBefore.children[0];r=s.target}else if(e.subform.children.length>=1&&e.subform.children[0].breakBefore.children.length>=1){s=e.subform.children[0].breakBefore.children[0];r=s.target}else if(e.break?.beforeTarget){s=e.break;r=s.beforeTarget}else if(e.subform.children.length>=1&&e.subform.children[0].break?.beforeTarget){s=e.subform.children[0].break;r=s.beforeTarget}if(s){const e=this[An](r,s[Dr]());if(e instanceof PageArea){a=e;s[gr]={}}}a||(a=t[0]);a[gr]={numberOfUse:1};const n=a[Dr]();n[gr]={numberOfUse:1,pageIndex:n.pageArea.children.indexOf(a),pageSetIndex:0};let g,o=null,c=null,C=!0,h=0,l=0;for(;;){if(C)h=0;else{i.children.pop();if(3==++h){warn("XFA - Something goes wrong: please file a bug.");return i}}g=null;this[gr].currentPageArea=a;const t=a[gn]().html;i.children.push(t);if(o){this[gr].noLayoutFailure=!0;t.children.push(o[gn](a[gr].space).html);o=null}if(c){this[gr].noLayoutFailure=!0;t.children.push(c[gn](a[gr].space).html);c=null}const s=a.contentArea.children,r=t.children.filter((e=>e.attributes.class.includes("xfaContentarea")));C=!1;this[gr].firstUnsplittable=null;this[gr].noLayoutFailure=!1;const flush=t=>{const i=e[Ir]();if(i){C||=i.children?.length>0;r[t].children.push(i)}};for(let t=l,a=s.length;t<a;t++){const a=this[gr].currentContentArea=s[t],n={width:a.w,height:a.h};l=0;if(o){r[t].children.push(o[gn](n).html);o=null}if(c){r[t].children.push(c[gn](n).html);c=null}const h=e[gn](n);if(h.success){if(h.html){C||=h.html.children?.length>0;r[t].children.push(h.html)}else!C&&i.children.length>1&&i.children.pop();return i}if(h.isBreak()){const e=h.breakNode;flush(t);if("auto"===e.targetType)continue;if(e.leader){o=this[An](e.leader,e[Dr]());o=o?o[0]:null}if(e.trailer){c=this[An](e.trailer,e[Dr]());c=c?c[0]:null}if("pageArea"===e.targetType){g=e[gr].target;t=1/0}else if(e[gr].target){g=e[gr].target;l=e[gr].index+1;t=1/0}else t=e[gr].index}else if(this[gr].overflowNode){const e=this[gr].overflowNode;this[gr].overflowNode=null;const i=e[dr](),a=i.target;i.addLeader=null!==i.leader;i.addTrailer=null!==i.trailer;flush(t);const r=t;t=1/0;if(a instanceof PageArea)g=a;else if(a instanceof ContentArea){const e=s.indexOf(a);if(-1!==e)e>r?t=e-1:l=e;else{g=a[Dr]();l=g.contentArea.children.indexOf(a)}}}else flush(t)}this[gr].pageNumber+=1;g&&(g[vr]()?g[gr].numberOfUse+=1:g=null);a=g||a[yr]();yield null}}}class Text extends ContentObject{constructor(e){super(Kn,"text");this.id=e.id||"";this.maxChars=getInteger({data:e.maxChars,defaultValue:0,validate:e=>e>=0});this.name=e.name||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}[Vs](){return!0}[Pr](e){if(e[Tr]===hn.xhtml.id){this[sr]=e;return!0}warn(`XFA - Invalid content in Text: ${e[qr]}.`);return!1}[jr](e){this[sr]instanceof XFAObject||super[jr](e)}[or](){"string"==typeof this[sr]&&(this[sr]=this[sr].replaceAll("\r\n","\n"))}[dr](){return"string"==typeof this[sr]?this[sr].split(/[\u2029\u2028\n]/).reduce(((e,t)=>{t&&e.push(t);return e}),[]).join("\n"):this[sr][rn]()}[gn](e){if("string"==typeof this[sr]){const e=valueToHtml(this[sr]).html;if(this[sr].includes("\u2029")){e.name="div";e.children=[];this[sr].split("\u2029").map((e=>e.split(/[\u2028\n]/).reduce(((e,t)=>{e.push({name:"span",value:t},{name:"br"});return e}),[]))).forEach((t=>{e.children.push({name:"p",children:t})}))}else if(/[\u2028\n]/.test(this[sr])){e.name="div";e.children=[];this[sr].split(/[\u2028\n]/).forEach((t=>{e.children.push({name:"span",value:t},{name:"br"})}))}return HTMLResult.success(e)}return this[sr][gn](e)}}class TextEdit extends XFAObject{constructor(e){super(Kn,"textEdit",!0);this.allowRichText=getInteger({data:e.allowRichText,defaultValue:0,validate:e=>1===e});this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.multiLine=getInteger({data:e.multiLine,defaultValue:"",validate:e=>0===e||1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.vScrollPolicy=getStringOption(e.vScrollPolicy,["auto","off","on"]);this.border=null;this.comb=null;this.extras=null;this.margin=null}[gn](e){const t=toStyle(this,"border","font","margin");let i;const a=this[Dr]()[Dr]();""===this.multiLine&&(this.multiLine=a instanceof Draw?1:0);i=1===this.multiLine?{name:"textarea",attributes:{dataId:a[rr]?.[cn]||a[cn],fieldId:a[cn],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}}:{name:"input",attributes:{type:"text",dataId:a[rr]?.[cn]||a[cn],fieldId:a[cn],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){i.attributes["aria-required"]=!0;i.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[i]})}}class Time extends StringObject{constructor(e){super(Kn,"time");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[or](){const e=this[sr].trim();this[sr]=e?new Date(e):null}[gn](e){return valueToHtml(this[sr]?this[sr].toString():"")}}class TimeStamp extends XFAObject{constructor(e){super(Kn,"timeStamp");this.id=e.id||"";this.server=e.server||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class ToolTip extends StringObject{constructor(e){super(Kn,"toolTip");this.id=e.id||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Traversal extends XFAObject{constructor(e){super(Kn,"traversal",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.traverse=new XFAObjectArray}}class Traverse extends XFAObject{constructor(e){super(Kn,"traverse",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["next","back","down","first","left","right","up"]);this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.script=null}get name(){return this.operation}[Yr](){return!1}}class Ui extends XFAObject{constructor(e){super(Kn,"ui",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null;this.barcode=null;this.button=null;this.checkButton=null;this.choiceList=null;this.dateTimeEdit=null;this.defaultUi=null;this.imageEdit=null;this.numericEdit=null;this.passwordEdit=null;this.signature=null;this.textEdit=null}[dr](){if(void 0===this[gr]){for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"picture"===e)continue;const t=this[e];if(t instanceof XFAObject){this[gr]=t;return t}}this[gr]=null}return this[gr]}[gn](e){const t=this[dr]();return t?t[gn](e):HTMLResult.EMPTY}}class Validate extends XFAObject{constructor(e){super(Kn,"validate",!0);this.formatTest=getStringOption(e.formatTest,["warning","disabled","error"]);this.id=e.id||"";this.nullTest=getStringOption(e.nullTest,["disabled","error","warning"]);this.scriptTest=getStringOption(e.scriptTest,["error","disabled","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.picture=null;this.script=null}}class Value extends XFAObject{constructor(e){super(Kn,"value",!0);this.id=e.id||"";this.override=getInteger({data:e.override,defaultValue:0,validate:e=>1===e});this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.arc=null;this.boolean=null;this.date=null;this.dateTime=null;this.decimal=null;this.exData=null;this.float=null;this.image=null;this.integer=null;this.line=null;this.rectangle=null;this.text=null;this.time=null}[an](e){const t=this[Dr]();if(t instanceof Field&&t.ui?.imageEdit){if(!this.image){this.image=new Image({});this[_s](this.image)}this.image[sr]=e[sr];return}const i=e[qr];if(null===this[i]){for(const e of Object.getOwnPropertyNames(this)){const t=this[e];if(t instanceof XFAObject){this[e]=null;this[zr](t)}}this[e[qr]]=e;this[_s](e)}else this[i][sr]=e[sr]}[rn](){if(this.exData)return"string"==typeof this.exData[sr]?this.exData[sr].trim():this.exData[sr][rn]().trim();for(const e of Object.getOwnPropertyNames(this)){if("image"===e)continue;const t=this[e];if(t instanceof XFAObject)return(t[sr]||"").toString().trim()}return null}[gn](e){for(const t of Object.getOwnPropertyNames(this)){const i=this[t];if(i instanceof XFAObject)return i[gn](e)}return HTMLResult.EMPTY}}class Variables extends XFAObject{constructor(e){super(Kn,"variables",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.manifest=new XFAObjectArray;this.script=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[Yr](){return!0}}class TemplateNamespace{static[Cn](e,t){if(TemplateNamespace.hasOwnProperty(e)){const i=TemplateNamespace[e](t);i[tn](t);return i}}static appearanceFilter(e){return new AppearanceFilter(e)}static arc(e){return new Arc(e)}static area(e){return new Area(e)}static assist(e){return new Assist(e)}static barcode(e){return new Barcode(e)}static bind(e){return new Bind(e)}static bindItems(e){return new BindItems(e)}static bookend(e){return new Bookend(e)}static boolean(e){return new BooleanElement(e)}static border(e){return new Border(e)}static break(e){return new Break(e)}static breakAfter(e){return new BreakAfter(e)}static breakBefore(e){return new BreakBefore(e)}static button(e){return new Button(e)}static calculate(e){return new Calculate(e)}static caption(e){return new Caption(e)}static certificate(e){return new Certificate(e)}static certificates(e){return new Certificates(e)}static checkButton(e){return new CheckButton(e)}static choiceList(e){return new ChoiceList(e)}static color(e){return new Color(e)}static comb(e){return new Comb(e)}static connect(e){return new Connect(e)}static contentArea(e){return new ContentArea(e)}static corner(e){return new Corner(e)}static date(e){return new DateElement(e)}static dateTime(e){return new DateTime(e)}static dateTimeEdit(e){return new DateTimeEdit(e)}static decimal(e){return new Decimal(e)}static defaultUi(e){return new DefaultUi(e)}static desc(e){return new Desc(e)}static digestMethod(e){return new DigestMethod(e)}static digestMethods(e){return new DigestMethods(e)}static draw(e){return new Draw(e)}static edge(e){return new Edge(e)}static encoding(e){return new Encoding(e)}static encodings(e){return new Encodings(e)}static encrypt(e){return new Encrypt(e)}static encryptData(e){return new EncryptData(e)}static encryption(e){return new Encryption(e)}static encryptionMethod(e){return new EncryptionMethod(e)}static encryptionMethods(e){return new EncryptionMethods(e)}static event(e){return new Event(e)}static exData(e){return new ExData(e)}static exObject(e){return new ExObject(e)}static exclGroup(e){return new ExclGroup(e)}static execute(e){return new Execute(e)}static extras(e){return new Extras(e)}static field(e){return new Field(e)}static fill(e){return new Fill(e)}static filter(e){return new Filter(e)}static float(e){return new Float(e)}static font(e){return new template_Font(e)}static format(e){return new Format(e)}static handler(e){return new Handler(e)}static hyphenation(e){return new Hyphenation(e)}static image(e){return new Image(e)}static imageEdit(e){return new ImageEdit(e)}static integer(e){return new Integer(e)}static issuers(e){return new Issuers(e)}static items(e){return new Items(e)}static keep(e){return new Keep(e)}static keyUsage(e){return new KeyUsage(e)}static line(e){return new Line(e)}static linear(e){return new Linear(e)}static lockDocument(e){return new LockDocument(e)}static manifest(e){return new Manifest(e)}static margin(e){return new Margin(e)}static mdp(e){return new Mdp(e)}static medium(e){return new Medium(e)}static message(e){return new Message(e)}static numericEdit(e){return new NumericEdit(e)}static occur(e){return new Occur(e)}static oid(e){return new Oid(e)}static oids(e){return new Oids(e)}static overflow(e){return new Overflow(e)}static pageArea(e){return new PageArea(e)}static pageSet(e){return new PageSet(e)}static para(e){return new Para(e)}static passwordEdit(e){return new PasswordEdit(e)}static pattern(e){return new template_Pattern(e)}static picture(e){return new Picture(e)}static proto(e){return new Proto(e)}static radial(e){return new Radial(e)}static reason(e){return new Reason(e)}static reasons(e){return new Reasons(e)}static rectangle(e){return new Rectangle(e)}static ref(e){return new RefElement(e)}static script(e){return new Script(e)}static setProperty(e){return new SetProperty(e)}static signData(e){return new SignData(e)}static signature(e){return new Signature(e)}static signing(e){return new Signing(e)}static solid(e){return new Solid(e)}static speak(e){return new Speak(e)}static stipple(e){return new Stipple(e)}static subform(e){return new Subform(e)}static subformSet(e){return new SubformSet(e)}static subjectDN(e){return new SubjectDN(e)}static subjectDNs(e){return new SubjectDNs(e)}static submit(e){return new Submit(e)}static template(e){return new Template(e)}static text(e){return new Text(e)}static textEdit(e){return new TextEdit(e)}static time(e){return new Time(e)}static timeStamp(e){return new TimeStamp(e)}static toolTip(e){return new ToolTip(e)}static traversal(e){return new Traversal(e)}static traverse(e){return new Traverse(e)}static ui(e){return new Ui(e)}static validate(e){return new Validate(e)}static value(e){return new Value(e)}static variables(e){return new Variables(e)}}const Wn=hn.datasets.id;function createText(e){const t=new Text({});t[sr]=e;return t}class Binder{constructor(e){this.root=e;this.datasets=e.datasets;this.data=e.datasets?.data||new XmlObject(hn.datasets.id,"data");this.emptyMerge=0===this.data[pr]().length;this.root.form=this.form=e.template[ir]()}_isConsumeData(){return!this.emptyMerge&&this._mergeMode}_isMatchTemplate(){return!this._isConsumeData()}bind(){this._bindElement(this.form,this.data);return this.form}getData(){return this.data}_bindValue(e,t,i){e[rr]=t;if(e[Sr]())if(t[Ur]()){const i=t[ur]();e[an](createText(i))}else if(e instanceof Field&&"multiSelect"===e.ui?.choiceList?.open){const i=t[pr]().map((e=>e[sr].trim())).join("\n");e[an](createText(i))}else this._isConsumeData()&&warn("XFA - Nodes haven't the same type.");else!t[Ur]()||this._isMatchTemplate()?this._bindElement(e,t):warn("XFA - Nodes haven't the same type.")}_findDataByNameToConsume(e,t,i,a){if(!e)return null;let s,r;for(let a=0;a<3;a++){s=i[fr](e,!1,!0);for(;;){r=s.next().value;if(!r)break;if(t===r[Ur]())return r}if(i[Tr]===hn.datasets.id&&"data"===i[qr])break;i=i[Dr]()}if(!a)return null;s=this.data[fr](e,!0,!1);r=s.next().value;if(r)return r;s=this.data[cr](e,!0);r=s.next().value;return r?.[Ur]()?r:null}_setProperties(e,t){if(e.hasOwnProperty("setProperty"))for(const{ref:i,target:a,connection:s}of e.setProperty.children){if(s)continue;if(!i)continue;const r=searchNode(this.root,t,i,!1,!1);if(!r){warn(`XFA - Invalid reference: ${i}.`);continue}const[n]=r;if(!n[Mr](this.data)){warn("XFA - Invalid node: must be a data node.");continue}const g=searchNode(this.root,e,a,!1,!1);if(!g){warn(`XFA - Invalid target: ${a}.`);continue}const[o]=g;if(!o[Mr](e)){warn("XFA - Invalid target: must be a property or subproperty.");continue}const c=o[Dr]();if(o instanceof SetProperty||c instanceof SetProperty){warn("XFA - Invalid target: cannot be a setProperty or one of its properties.");continue}if(o instanceof BindItems||c instanceof BindItems){warn("XFA - Invalid target: cannot be a bindItems or one of its properties.");continue}const C=n[rn](),h=o[qr];if(o instanceof XFAAttribute){const e=Object.create(null);e[h]=C;const t=Reflect.construct(Object.getPrototypeOf(c).constructor,[e]);c[h]=t[h]}else if(o.hasOwnProperty(sr)){o[rr]=n;o[sr]=C;o[or]()}else warn("XFA - Invalid node to use in setProperty")}}_bindItems(e,t){if(!e.hasOwnProperty("items")||!e.hasOwnProperty("bindItems")||e.bindItems.isEmpty())return;for(const t of e.items.children)e[zr](t);e.items.clear();const i=new Items({}),a=new Items({});e[_s](i);e.items.push(i);e[_s](a);e.items.push(a);for(const{ref:s,labelRef:r,valueRef:n,connection:g}of e.bindItems.children){if(g)continue;if(!s)continue;const e=searchNode(this.root,t,s,!1,!1);if(e)for(const t of e){if(!t[Mr](this.datasets)){warn(`XFA - Invalid ref (${s}): must be a datasets child.`);continue}const e=searchNode(this.root,t,r,!0,!1);if(!e){warn(`XFA - Invalid label: ${r}.`);continue}const[g]=e;if(!g[Mr](this.datasets)){warn("XFA - Invalid label: must be a datasets child.");continue}const o=searchNode(this.root,t,n,!0,!1);if(!o){warn(`XFA - Invalid value: ${n}.`);continue}const[c]=o;if(!c[Mr](this.datasets)){warn("XFA - Invalid value: must be a datasets child.");continue}const C=createText(g[rn]()),h=createText(c[rn]());i[_s](C);i.text.push(C);a[_s](h);a.text.push(h)}else warn(`XFA - Invalid reference: ${s}.`)}}_bindOccurrences(e,t,i){let a;if(t.length>1){a=e[ir]();a[zr](a.occur);a.occur=null}this._bindValue(e,t[0],i);this._setProperties(e,t[0]);this._bindItems(e,t[0]);if(1===t.length)return;const s=e[Dr](),r=e[qr],n=s[Rr](e);for(let e=1,g=t.length;e<g;e++){const g=t[e],o=a[ir]();s[r].push(o);s[Nr](n+e,o);this._bindValue(o,g,i);this._setProperties(o,g);this._bindItems(o,g)}}_createOccurrences(e){if(!this.emptyMerge)return;const{occur:t}=e;if(!t||t.initial<=1)return;const i=e[Dr](),a=e[qr];if(!(i[a]instanceof XFAObjectArray))return;let s;s=e.name?i[a].children.filter((t=>t.name===e.name)).length:i[a].children.length;const r=i[Rr](e)+1,n=t.initial-s;if(n){const t=e[ir]();t[zr](t.occur);t.occur=null;i[a].push(t);i[Nr](r,t);for(let e=1;e<n;e++){const s=t[ir]();i[a].push(s);i[Nr](r+e,s)}}}_getOccurInfo(e){const{name:t,occur:i}=e;if(!i||!t)return[1,1];const a=-1===i.max?1/0:i.max;return[i.min,a]}_setAndBind(e,t){this._setProperties(e,t);this._bindItems(e,t);this._bindElement(e,t)}_bindElement(e,t){const i=[];this._createOccurrences(e);for(const a of e[pr]()){if(a[rr])continue;if(void 0===this._mergeMode&&"subform"===a[qr]){this._mergeMode="consumeData"===a.mergeMode;const e=t[pr]();if(e.length>0)this._bindOccurrences(a,[e[0]],null);else if(this.emptyMerge){const e=t[Tr]===Wn?-1:t[Tr],i=a[rr]=new XmlObject(e,a.name||"root");t[_s](i);this._bindElement(a,i)}continue}if(!a[xr]())continue;let e=!1,s=null,r=null,n=null;if(a.bind){switch(a.bind.match){case"none":this._setAndBind(a,t);continue;case"global":e=!0;break;case"dataRef":if(!a.bind.ref){warn(`XFA - ref is empty in node ${a[qr]}.`);this._setAndBind(a,t);continue}r=a.bind.ref}a.bind.picture&&(s=a.bind.picture[sr])}const[g,o]=this._getOccurInfo(a);if(r){n=searchNode(this.root,t,r,!0,!1);if(null===n){n=createDataNode(this.data,t,r);if(!n)continue;this._isConsumeData()&&(n[ar]=!0);this._setAndBind(a,n);continue}this._isConsumeData()&&(n=n.filter((e=>!e[ar])));n.length>o?n=n.slice(0,o):0===n.length&&(n=null);n&&this._isConsumeData()&&n.forEach((e=>{e[ar]=!0}))}else{if(!a.name){this._setAndBind(a,t);continue}if(this._isConsumeData()){const i=[];for(;i.length<o;){const s=this._findDataByNameToConsume(a.name,a[Sr](),t,e);if(!s)break;s[ar]=!0;i.push(s)}n=i.length>0?i:null}else{n=t[fr](a.name,!1,this.emptyMerge).next().value;if(!n){if(0===g){i.push(a);continue}const e=t[Tr]===Wn?-1:t[Tr];n=a[rr]=new XmlObject(e,a.name);this.emptyMerge&&(n[ar]=!0);t[_s](n);this._setAndBind(a,n);continue}this.emptyMerge&&(n[ar]=!0);n=[n]}}n?this._bindOccurrences(a,n,s):g>0?this._setAndBind(a,t):i.push(a)}i.forEach((e=>e[Dr]()[zr](e)))}}class DataHandler{constructor(e,t){this.data=t;this.dataset=e.datasets||null}serialize(e){const t=[[-1,this.data[pr]()]];for(;t.length>0;){const i=t.at(-1),[a,s]=i;if(a+1===s.length){t.pop();continue}const r=s[++i[0]],n=e.get(r[cn]);if(n)r[an](n);else{const t=r[Cr]();for(const i of t.values()){const t=e.get(i[cn]);if(t){i[an](t);break}}}const g=r[pr]();g.length>0&&t.push([-1,g])}const i=['<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">'];if(this.dataset)for(const e of this.dataset[pr]())"data"!==e[qr]&&e[on](i);this.data[on](i);i.push("</xfa:datasets>");return i.join("")}}const jn=hn.config.id;class Acrobat extends XFAObject{constructor(e){super(jn,"acrobat",!0);this.acrobat7=null;this.autoSave=null;this.common=null;this.validate=null;this.validateApprovalSignatures=null;this.submitUrl=new XFAObjectArray}}class Acrobat7 extends XFAObject{constructor(e){super(jn,"acrobat7",!0);this.dynamicRender=null}}class ADBE_JSConsole extends OptionObject{constructor(e){super(jn,"ADBE_JSConsole",["delegate","Enable","Disable"])}}class ADBE_JSDebugger extends OptionObject{constructor(e){super(jn,"ADBE_JSDebugger",["delegate","Enable","Disable"])}}class AddSilentPrint extends Option01{constructor(e){super(jn,"addSilentPrint")}}class AddViewerPreferences extends Option01{constructor(e){super(jn,"addViewerPreferences")}}class AdjustData extends Option10{constructor(e){super(jn,"adjustData")}}class AdobeExtensionLevel extends IntegerObject{constructor(e){super(jn,"adobeExtensionLevel",0,(e=>e>=1&&e<=8))}}class Agent extends XFAObject{constructor(e){super(jn,"agent",!0);this.name=e.name?e.name.trim():"";this.common=new XFAObjectArray}}class AlwaysEmbed extends ContentObject{constructor(e){super(jn,"alwaysEmbed")}}class Amd extends StringObject{constructor(e){super(jn,"amd")}}class config_Area extends XFAObject{constructor(e){super(jn,"area");this.level=getInteger({data:e.level,defaultValue:0,validate:e=>e>=1&&e<=3});this.name=getStringOption(e.name,["","barcode","coreinit","deviceDriver","font","general","layout","merge","script","signature","sourceSet","templateCache"])}}class Attributes extends OptionObject{constructor(e){super(jn,"attributes",["preserve","delegate","ignore"])}}class AutoSave extends OptionObject{constructor(e){super(jn,"autoSave",["disabled","enabled"])}}class Base extends StringObject{constructor(e){super(jn,"base")}}class BatchOutput extends XFAObject{constructor(e){super(jn,"batchOutput");this.format=getStringOption(e.format,["none","concat","zip","zipCompress"])}}class BehaviorOverride extends ContentObject{constructor(e){super(jn,"behaviorOverride")}[or](){this[sr]=new Map(this[sr].trim().split(/\s+/).filter((e=>e.includes(":"))).map((e=>e.split(":",2))))}}class Cache extends XFAObject{constructor(e){super(jn,"cache",!0);this.templateCache=null}}class Change extends Option01{constructor(e){super(jn,"change")}}class Common extends XFAObject{constructor(e){super(jn,"common",!0);this.data=null;this.locale=null;this.localeSet=null;this.messaging=null;this.suppressBanner=null;this.template=null;this.validationMessaging=null;this.versionControl=null;this.log=new XFAObjectArray}}class Compress extends XFAObject{constructor(e){super(jn,"compress");this.scope=getStringOption(e.scope,["imageOnly","document"])}}class CompressLogicalStructure extends Option01{constructor(e){super(jn,"compressLogicalStructure")}}class CompressObjectStream extends Option10{constructor(e){super(jn,"compressObjectStream")}}class Compression extends XFAObject{constructor(e){super(jn,"compression",!0);this.compressLogicalStructure=null;this.compressObjectStream=null;this.level=null;this.type=null}}class Config extends XFAObject{constructor(e){super(jn,"config",!0);this.acrobat=null;this.present=null;this.trace=null;this.agent=new XFAObjectArray}}class Conformance extends OptionObject{constructor(e){super(jn,"conformance",["A","B"])}}class ContentCopy extends Option01{constructor(e){super(jn,"contentCopy")}}class Copies extends IntegerObject{constructor(e){super(jn,"copies",1,(e=>e>=1))}}class Creator extends StringObject{constructor(e){super(jn,"creator")}}class CurrentPage extends IntegerObject{constructor(e){super(jn,"currentPage",0,(e=>e>=0))}}class Data extends XFAObject{constructor(e){super(jn,"data",!0);this.adjustData=null;this.attributes=null;this.incrementalLoad=null;this.outputXSL=null;this.range=null;this.record=null;this.startNode=null;this.uri=null;this.window=null;this.xsl=null;this.excludeNS=new XFAObjectArray;this.transform=new XFAObjectArray}}class Debug extends XFAObject{constructor(e){super(jn,"debug",!0);this.uri=null}}class DefaultTypeface extends ContentObject{constructor(e){super(jn,"defaultTypeface");this.writingScript=getStringOption(e.writingScript,["*","Arabic","Cyrillic","EastEuropeanRoman","Greek","Hebrew","Japanese","Korean","Roman","SimplifiedChinese","Thai","TraditionalChinese","Vietnamese"])}}class Destination extends OptionObject{constructor(e){super(jn,"destination",["pdf","pcl","ps","webClient","zpl"])}}class DocumentAssembly extends Option01{constructor(e){super(jn,"documentAssembly")}}class Driver extends XFAObject{constructor(e){super(jn,"driver",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class DuplexOption extends OptionObject{constructor(e){super(jn,"duplexOption",["simplex","duplexFlipLongEdge","duplexFlipShortEdge"])}}class DynamicRender extends OptionObject{constructor(e){super(jn,"dynamicRender",["forbidden","required"])}}class Embed extends Option01{constructor(e){super(jn,"embed")}}class config_Encrypt extends Option01{constructor(e){super(jn,"encrypt")}}class config_Encryption extends XFAObject{constructor(e){super(jn,"encryption",!0);this.encrypt=null;this.encryptionLevel=null;this.permissions=null}}class EncryptionLevel extends OptionObject{constructor(e){super(jn,"encryptionLevel",["40bit","128bit"])}}class Enforce extends StringObject{constructor(e){super(jn,"enforce")}}class Equate extends XFAObject{constructor(e){super(jn,"equate");this.force=getInteger({data:e.force,defaultValue:1,validate:e=>0===e});this.from=e.from||"";this.to=e.to||""}}class EquateRange extends XFAObject{constructor(e){super(jn,"equateRange");this.from=e.from||"";this.to=e.to||"";this._unicodeRange=e.unicodeRange||""}get unicodeRange(){const e=[],t=/U\+([0-9a-fA-F]+)/,i=this._unicodeRange;for(let a of i.split(",").map((e=>e.trim())).filter((e=>!!e))){a=a.split("-",2).map((e=>{const i=e.match(t);return i?parseInt(i[1],16):0}));1===a.length&&a.push(a[0]);e.push(a)}return shadow(this,"unicodeRange",e)}}class Exclude extends ContentObject{constructor(e){super(jn,"exclude")}[or](){this[sr]=this[sr].trim().split(/\s+/).filter((e=>e&&["calculate","close","enter","exit","initialize","ready","validate"].includes(e)))}}class ExcludeNS extends StringObject{constructor(e){super(jn,"excludeNS")}}class FlipLabel extends OptionObject{constructor(e){super(jn,"flipLabel",["usePrinterSetting","on","off"])}}class config_FontInfo extends XFAObject{constructor(e){super(jn,"fontInfo",!0);this.embed=null;this.map=null;this.subsetBelow=null;this.alwaysEmbed=new XFAObjectArray;this.defaultTypeface=new XFAObjectArray;this.neverEmbed=new XFAObjectArray}}class FormFieldFilling extends Option01{constructor(e){super(jn,"formFieldFilling")}}class GroupParent extends StringObject{constructor(e){super(jn,"groupParent")}}class IfEmpty extends OptionObject{constructor(e){super(jn,"ifEmpty",["dataValue","dataGroup","ignore","remove"])}}class IncludeXDPContent extends StringObject{constructor(e){super(jn,"includeXDPContent")}}class IncrementalLoad extends OptionObject{constructor(e){super(jn,"incrementalLoad",["none","forwardOnly"])}}class IncrementalMerge extends Option01{constructor(e){super(jn,"incrementalMerge")}}class Interactive extends Option01{constructor(e){super(jn,"interactive")}}class Jog extends OptionObject{constructor(e){super(jn,"jog",["usePrinterSetting","none","pageSet"])}}class LabelPrinter extends XFAObject{constructor(e){super(jn,"labelPrinter",!0);this.name=getStringOption(e.name,["zpl","dpl","ipl","tcpl"]);this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class Layout extends OptionObject{constructor(e){super(jn,"layout",["paginate","panel"])}}class Level extends IntegerObject{constructor(e){super(jn,"level",0,(e=>e>0))}}class Linearized extends Option01{constructor(e){super(jn,"linearized")}}class Locale extends StringObject{constructor(e){super(jn,"locale")}}class LocaleSet extends StringObject{constructor(e){super(jn,"localeSet")}}class Log extends XFAObject{constructor(e){super(jn,"log",!0);this.mode=null;this.threshold=null;this.to=null;this.uri=null}}class MapElement extends XFAObject{constructor(e){super(jn,"map",!0);this.equate=new XFAObjectArray;this.equateRange=new XFAObjectArray}}class MediumInfo extends XFAObject{constructor(e){super(jn,"mediumInfo",!0);this.map=null}}class config_Message extends XFAObject{constructor(e){super(jn,"message",!0);this.msgId=null;this.severity=null}}class Messaging extends XFAObject{constructor(e){super(jn,"messaging",!0);this.message=new XFAObjectArray}}class Mode extends OptionObject{constructor(e){super(jn,"mode",["append","overwrite"])}}class ModifyAnnots extends Option01{constructor(e){super(jn,"modifyAnnots")}}class MsgId extends IntegerObject{constructor(e){super(jn,"msgId",1,(e=>e>=1))}}class NameAttr extends StringObject{constructor(e){super(jn,"nameAttr")}}class NeverEmbed extends ContentObject{constructor(e){super(jn,"neverEmbed")}}class NumberOfCopies extends IntegerObject{constructor(e){super(jn,"numberOfCopies",null,(e=>e>=2&&e<=5))}}class OpenAction extends XFAObject{constructor(e){super(jn,"openAction",!0);this.destination=null}}class Output extends XFAObject{constructor(e){super(jn,"output",!0);this.to=null;this.type=null;this.uri=null}}class OutputBin extends StringObject{constructor(e){super(jn,"outputBin")}}class OutputXSL extends XFAObject{constructor(e){super(jn,"outputXSL",!0);this.uri=null}}class Overprint extends OptionObject{constructor(e){super(jn,"overprint",["none","both","draw","field"])}}class Packets extends StringObject{constructor(e){super(jn,"packets")}[or](){"*"!==this[sr]&&(this[sr]=this[sr].trim().split(/\s+/).filter((e=>["config","datasets","template","xfdf","xslt"].includes(e))))}}class PageOffset extends XFAObject{constructor(e){super(jn,"pageOffset");this.x=getInteger({data:e.x,defaultValue:"useXDCSetting",validate:e=>!0});this.y=getInteger({data:e.y,defaultValue:"useXDCSetting",validate:e=>!0})}}class PageRange extends StringObject{constructor(e){super(jn,"pageRange")}[or](){const e=this[sr].trim().split(/\s+/).map((e=>parseInt(e,10))),t=[];for(let i=0,a=e.length;i<a;i+=2)t.push(e.slice(i,i+2));this[sr]=t}}class Pagination extends OptionObject{constructor(e){super(jn,"pagination",["simplex","duplexShortEdge","duplexLongEdge"])}}class PaginationOverride extends OptionObject{constructor(e){super(jn,"paginationOverride",["none","forceDuplex","forceDuplexLongEdge","forceDuplexShortEdge","forceSimplex"])}}class Part extends IntegerObject{constructor(e){super(jn,"part",1,(e=>!1))}}class Pcl extends XFAObject{constructor(e){super(jn,"pcl",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.pageOffset=null;this.staple=null;this.xdc=null}}class Pdf extends XFAObject{constructor(e){super(jn,"pdf",!0);this.name=e.name||"";this.adobeExtensionLevel=null;this.batchOutput=null;this.compression=null;this.creator=null;this.encryption=null;this.fontInfo=null;this.interactive=null;this.linearized=null;this.openAction=null;this.pdfa=null;this.producer=null;this.renderPolicy=null;this.scriptModel=null;this.silentPrint=null;this.submitFormat=null;this.tagged=null;this.version=null;this.viewerPreferences=null;this.xdc=null}}class Pdfa extends XFAObject{constructor(e){super(jn,"pdfa",!0);this.amd=null;this.conformance=null;this.includeXDPContent=null;this.part=null}}class Permissions extends XFAObject{constructor(e){super(jn,"permissions",!0);this.accessibleContent=null;this.change=null;this.contentCopy=null;this.documentAssembly=null;this.formFieldFilling=null;this.modifyAnnots=null;this.plaintextMetadata=null;this.print=null;this.printHighQuality=null}}class PickTrayByPDFSize extends Option01{constructor(e){super(jn,"pickTrayByPDFSize")}}class config_Picture extends StringObject{constructor(e){super(jn,"picture")}}class PlaintextMetadata extends Option01{constructor(e){super(jn,"plaintextMetadata")}}class Presence extends OptionObject{constructor(e){super(jn,"presence",["preserve","dissolve","dissolveStructure","ignore","remove"])}}class Present extends XFAObject{constructor(e){super(jn,"present",!0);this.behaviorOverride=null;this.cache=null;this.common=null;this.copies=null;this.destination=null;this.incrementalMerge=null;this.layout=null;this.output=null;this.overprint=null;this.pagination=null;this.paginationOverride=null;this.script=null;this.validate=null;this.xdp=null;this.driver=new XFAObjectArray;this.labelPrinter=new XFAObjectArray;this.pcl=new XFAObjectArray;this.pdf=new XFAObjectArray;this.ps=new XFAObjectArray;this.submitUrl=new XFAObjectArray;this.webClient=new XFAObjectArray;this.zpl=new XFAObjectArray}}class Print extends Option01{constructor(e){super(jn,"print")}}class PrintHighQuality extends Option01{constructor(e){super(jn,"printHighQuality")}}class PrintScaling extends OptionObject{constructor(e){super(jn,"printScaling",["appdefault","noScaling"])}}class PrinterName extends StringObject{constructor(e){super(jn,"printerName")}}class Producer extends StringObject{constructor(e){super(jn,"producer")}}class Ps extends XFAObject{constructor(e){super(jn,"ps",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.staple=null;this.xdc=null}}class Range extends ContentObject{constructor(e){super(jn,"range")}[or](){this[sr]=this[sr].trim().split(/\s*,\s*/,2).map((e=>e.split("-").map((e=>parseInt(e.trim(),10))))).filter((e=>e.every((e=>!isNaN(e))))).map((e=>{1===e.length&&e.push(e[0]);return e}))}}class Record extends ContentObject{constructor(e){super(jn,"record")}[or](){this[sr]=this[sr].trim();const e=parseInt(this[sr],10);!isNaN(e)&&e>=0&&(this[sr]=e)}}class Relevant extends ContentObject{constructor(e){super(jn,"relevant")}[or](){this[sr]=this[sr].trim().split(/\s+/)}}class Rename extends ContentObject{constructor(e){super(jn,"rename")}[or](){this[sr]=this[sr].trim();(this[sr].toLowerCase().startsWith("xml")||new RegExp("[\\p{L}_][\\p{L}\\d._\\p{M}-]*","u").test(this[sr]))&&warn("XFA - Rename: invalid XFA name")}}class RenderPolicy extends OptionObject{constructor(e){super(jn,"renderPolicy",["server","client"])}}class RunScripts extends OptionObject{constructor(e){super(jn,"runScripts",["both","client","none","server"])}}class config_Script extends XFAObject{constructor(e){super(jn,"script",!0);this.currentPage=null;this.exclude=null;this.runScripts=null}}class ScriptModel extends OptionObject{constructor(e){super(jn,"scriptModel",["XFA","none"])}}class Severity extends OptionObject{constructor(e){super(jn,"severity",["ignore","error","information","trace","warning"])}}class SilentPrint extends XFAObject{constructor(e){super(jn,"silentPrint",!0);this.addSilentPrint=null;this.printerName=null}}class Staple extends XFAObject{constructor(e){super(jn,"staple");this.mode=getStringOption(e.mode,["usePrinterSetting","on","off"])}}class StartNode extends StringObject{constructor(e){super(jn,"startNode")}}class StartPage extends IntegerObject{constructor(e){super(jn,"startPage",0,(e=>!0))}}class SubmitFormat extends OptionObject{constructor(e){super(jn,"submitFormat",["html","delegate","fdf","xml","pdf"])}}class SubmitUrl extends StringObject{constructor(e){super(jn,"submitUrl")}}class SubsetBelow extends IntegerObject{constructor(e){super(jn,"subsetBelow",100,(e=>e>=0&&e<=100))}}class SuppressBanner extends Option01{constructor(e){super(jn,"suppressBanner")}}class Tagged extends Option01{constructor(e){super(jn,"tagged")}}class config_Template extends XFAObject{constructor(e){super(jn,"template",!0);this.base=null;this.relevant=null;this.startPage=null;this.uri=null;this.xsl=null}}class Threshold extends OptionObject{constructor(e){super(jn,"threshold",["trace","error","information","warning"])}}class To extends OptionObject{constructor(e){super(jn,"to",["null","memory","stderr","stdout","system","uri"])}}class TemplateCache extends XFAObject{constructor(e){super(jn,"templateCache");this.maxEntries=getInteger({data:e.maxEntries,defaultValue:5,validate:e=>e>=0})}}class Trace extends XFAObject{constructor(e){super(jn,"trace",!0);this.area=new XFAObjectArray}}class Transform extends XFAObject{constructor(e){super(jn,"transform",!0);this.groupParent=null;this.ifEmpty=null;this.nameAttr=null;this.picture=null;this.presence=null;this.rename=null;this.whitespace=null}}class Type extends OptionObject{constructor(e){super(jn,"type",["none","ascii85","asciiHex","ccittfax","flate","lzw","runLength","native","xdp","mergedXDP"])}}class Uri extends StringObject{constructor(e){super(jn,"uri")}}class config_Validate extends OptionObject{constructor(e){super(jn,"validate",["preSubmit","prePrint","preExecute","preSave"])}}class ValidateApprovalSignatures extends ContentObject{constructor(e){super(jn,"validateApprovalSignatures")}[or](){this[sr]=this[sr].trim().split(/\s+/).filter((e=>["docReady","postSign"].includes(e)))}}class ValidationMessaging extends OptionObject{constructor(e){super(jn,"validationMessaging",["allMessagesIndividually","allMessagesTogether","firstMessageOnly","noMessages"])}}class Version extends OptionObject{constructor(e){super(jn,"version",["1.7","1.6","1.5","1.4","1.3","1.2"])}}class VersionControl extends XFAObject{constructor(e){super(jn,"VersionControl");this.outputBelow=getStringOption(e.outputBelow,["warn","error","update"]);this.sourceAbove=getStringOption(e.sourceAbove,["warn","error"]);this.sourceBelow=getStringOption(e.sourceBelow,["update","maintain"])}}class ViewerPreferences extends XFAObject{constructor(e){super(jn,"viewerPreferences",!0);this.ADBE_JSConsole=null;this.ADBE_JSDebugger=null;this.addViewerPreferences=null;this.duplexOption=null;this.enforce=null;this.numberOfCopies=null;this.pageRange=null;this.pickTrayByPDFSize=null;this.printScaling=null}}class WebClient extends XFAObject{constructor(e){super(jn,"webClient",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class Whitespace extends OptionObject{constructor(e){super(jn,"whitespace",["preserve","ltrim","normalize","rtrim","trim"])}}class Window extends ContentObject{constructor(e){super(jn,"window")}[or](){const e=this[sr].trim().split(/\s*,\s*/,2).map((e=>parseInt(e,10)));if(e.some((e=>isNaN(e))))this[sr]=[0,0];else{1===e.length&&e.push(e[0]);this[sr]=e}}}class Xdc extends XFAObject{constructor(e){super(jn,"xdc",!0);this.uri=new XFAObjectArray;this.xsl=new XFAObjectArray}}class Xdp extends XFAObject{constructor(e){super(jn,"xdp",!0);this.packets=null}}class Xsl extends XFAObject{constructor(e){super(jn,"xsl",!0);this.debug=null;this.uri=null}}class Zpl extends XFAObject{constructor(e){super(jn,"zpl",!0);this.name=e.name?e.name.trim():"";this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class ConfigNamespace{static[Cn](e,t){if(ConfigNamespace.hasOwnProperty(e))return ConfigNamespace[e](t)}static acrobat(e){return new Acrobat(e)}static acrobat7(e){return new Acrobat7(e)}static ADBE_JSConsole(e){return new ADBE_JSConsole(e)}static ADBE_JSDebugger(e){return new ADBE_JSDebugger(e)}static addSilentPrint(e){return new AddSilentPrint(e)}static addViewerPreferences(e){return new AddViewerPreferences(e)}static adjustData(e){return new AdjustData(e)}static adobeExtensionLevel(e){return new AdobeExtensionLevel(e)}static agent(e){return new Agent(e)}static alwaysEmbed(e){return new AlwaysEmbed(e)}static amd(e){return new Amd(e)}static area(e){return new config_Area(e)}static attributes(e){return new Attributes(e)}static autoSave(e){return new AutoSave(e)}static base(e){return new Base(e)}static batchOutput(e){return new BatchOutput(e)}static behaviorOverride(e){return new BehaviorOverride(e)}static cache(e){return new Cache(e)}static change(e){return new Change(e)}static common(e){return new Common(e)}static compress(e){return new Compress(e)}static compressLogicalStructure(e){return new CompressLogicalStructure(e)}static compressObjectStream(e){return new CompressObjectStream(e)}static compression(e){return new Compression(e)}static config(e){return new Config(e)}static conformance(e){return new Conformance(e)}static contentCopy(e){return new ContentCopy(e)}static copies(e){return new Copies(e)}static creator(e){return new Creator(e)}static currentPage(e){return new CurrentPage(e)}static data(e){return new Data(e)}static debug(e){return new Debug(e)}static defaultTypeface(e){return new DefaultTypeface(e)}static destination(e){return new Destination(e)}static documentAssembly(e){return new DocumentAssembly(e)}static driver(e){return new Driver(e)}static duplexOption(e){return new DuplexOption(e)}static dynamicRender(e){return new DynamicRender(e)}static embed(e){return new Embed(e)}static encrypt(e){return new config_Encrypt(e)}static encryption(e){return new config_Encryption(e)}static encryptionLevel(e){return new EncryptionLevel(e)}static enforce(e){return new Enforce(e)}static equate(e){return new Equate(e)}static equateRange(e){return new EquateRange(e)}static exclude(e){return new Exclude(e)}static excludeNS(e){return new ExcludeNS(e)}static flipLabel(e){return new FlipLabel(e)}static fontInfo(e){return new config_FontInfo(e)}static formFieldFilling(e){return new FormFieldFilling(e)}static groupParent(e){return new GroupParent(e)}static ifEmpty(e){return new IfEmpty(e)}static includeXDPContent(e){return new IncludeXDPContent(e)}static incrementalLoad(e){return new IncrementalLoad(e)}static incrementalMerge(e){return new IncrementalMerge(e)}static interactive(e){return new Interactive(e)}static jog(e){return new Jog(e)}static labelPrinter(e){return new LabelPrinter(e)}static layout(e){return new Layout(e)}static level(e){return new Level(e)}static linearized(e){return new Linearized(e)}static locale(e){return new Locale(e)}static localeSet(e){return new LocaleSet(e)}static log(e){return new Log(e)}static map(e){return new MapElement(e)}static mediumInfo(e){return new MediumInfo(e)}static message(e){return new config_Message(e)}static messaging(e){return new Messaging(e)}static mode(e){return new Mode(e)}static modifyAnnots(e){return new ModifyAnnots(e)}static msgId(e){return new MsgId(e)}static nameAttr(e){return new NameAttr(e)}static neverEmbed(e){return new NeverEmbed(e)}static numberOfCopies(e){return new NumberOfCopies(e)}static openAction(e){return new OpenAction(e)}static output(e){return new Output(e)}static outputBin(e){return new OutputBin(e)}static outputXSL(e){return new OutputXSL(e)}static overprint(e){return new Overprint(e)}static packets(e){return new Packets(e)}static pageOffset(e){return new PageOffset(e)}static pageRange(e){return new PageRange(e)}static pagination(e){return new Pagination(e)}static paginationOverride(e){return new PaginationOverride(e)}static part(e){return new Part(e)}static pcl(e){return new Pcl(e)}static pdf(e){return new Pdf(e)}static pdfa(e){return new Pdfa(e)}static permissions(e){return new Permissions(e)}static pickTrayByPDFSize(e){return new PickTrayByPDFSize(e)}static picture(e){return new config_Picture(e)}static plaintextMetadata(e){return new PlaintextMetadata(e)}static presence(e){return new Presence(e)}static present(e){return new Present(e)}static print(e){return new Print(e)}static printHighQuality(e){return new PrintHighQuality(e)}static printScaling(e){return new PrintScaling(e)}static printerName(e){return new PrinterName(e)}static producer(e){return new Producer(e)}static ps(e){return new Ps(e)}static range(e){return new Range(e)}static record(e){return new Record(e)}static relevant(e){return new Relevant(e)}static rename(e){return new Rename(e)}static renderPolicy(e){return new RenderPolicy(e)}static runScripts(e){return new RunScripts(e)}static script(e){return new config_Script(e)}static scriptModel(e){return new ScriptModel(e)}static severity(e){return new Severity(e)}static silentPrint(e){return new SilentPrint(e)}static staple(e){return new Staple(e)}static startNode(e){return new StartNode(e)}static startPage(e){return new StartPage(e)}static submitFormat(e){return new SubmitFormat(e)}static submitUrl(e){return new SubmitUrl(e)}static subsetBelow(e){return new SubsetBelow(e)}static suppressBanner(e){return new SuppressBanner(e)}static tagged(e){return new Tagged(e)}static template(e){return new config_Template(e)}static templateCache(e){return new TemplateCache(e)}static threshold(e){return new Threshold(e)}static to(e){return new To(e)}static trace(e){return new Trace(e)}static transform(e){return new Transform(e)}static type(e){return new Type(e)}static uri(e){return new Uri(e)}static validate(e){return new config_Validate(e)}static validateApprovalSignatures(e){return new ValidateApprovalSignatures(e)}static validationMessaging(e){return new ValidationMessaging(e)}static version(e){return new Version(e)}static versionControl(e){return new VersionControl(e)}static viewerPreferences(e){return new ViewerPreferences(e)}static webClient(e){return new WebClient(e)}static whitespace(e){return new Whitespace(e)}static window(e){return new Window(e)}static xdc(e){return new Xdc(e)}static xdp(e){return new Xdp(e)}static xsl(e){return new Xsl(e)}static zpl(e){return new Zpl(e)}}const Xn=hn.connectionSet.id;class ConnectionSet extends XFAObject{constructor(e){super(Xn,"connectionSet",!0);this.wsdlConnection=new XFAObjectArray;this.xmlConnection=new XFAObjectArray;this.xsdConnection=new XFAObjectArray}}class EffectiveInputPolicy extends XFAObject{constructor(e){super(Xn,"effectiveInputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EffectiveOutputPolicy extends XFAObject{constructor(e){super(Xn,"effectiveOutputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Operation extends StringObject{constructor(e){super(Xn,"operation");this.id=e.id||"";this.input=e.input||"";this.name=e.name||"";this.output=e.output||"";this.use=e.use||"";this.usehref=e.usehref||""}}class RootElement extends StringObject{constructor(e){super(Xn,"rootElement");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAction extends StringObject{constructor(e){super(Xn,"soapAction");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAddress extends StringObject{constructor(e){super(Xn,"soapAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class connection_set_Uri extends StringObject{constructor(e){super(Xn,"uri");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlAddress extends StringObject{constructor(e){super(Xn,"wsdlAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlConnection extends XFAObject{constructor(e){super(Xn,"wsdlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.effectiveInputPolicy=null;this.effectiveOutputPolicy=null;this.operation=null;this.soapAction=null;this.soapAddress=null;this.wsdlAddress=null}}class XmlConnection extends XFAObject{constructor(e){super(Xn,"xmlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.uri=null}}class XsdConnection extends XFAObject{constructor(e){super(Xn,"xsdConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.rootElement=null;this.uri=null}}class ConnectionSetNamespace{static[Cn](e,t){if(ConnectionSetNamespace.hasOwnProperty(e))return ConnectionSetNamespace[e](t)}static connectionSet(e){return new ConnectionSet(e)}static effectiveInputPolicy(e){return new EffectiveInputPolicy(e)}static effectiveOutputPolicy(e){return new EffectiveOutputPolicy(e)}static operation(e){return new Operation(e)}static rootElement(e){return new RootElement(e)}static soapAction(e){return new SoapAction(e)}static soapAddress(e){return new SoapAddress(e)}static uri(e){return new connection_set_Uri(e)}static wsdlAddress(e){return new WsdlAddress(e)}static wsdlConnection(e){return new WsdlConnection(e)}static xmlConnection(e){return new XmlConnection(e)}static xsdConnection(e){return new XsdConnection(e)}}const Zn=hn.datasets.id;class datasets_Data extends XmlObject{constructor(e){super(Zn,"data",e)}[Lr](){return!0}}class Datasets extends XFAObject{constructor(e){super(Zn,"datasets",!0);this.data=null;this.Signature=null}[Pr](e){const t=e[qr];("data"===t&&e[Tr]===Zn||"Signature"===t&&e[Tr]===hn.signature.id)&&(this[t]=e);this[_s](e)}}class DatasetsNamespace{static[Cn](e,t){if(DatasetsNamespace.hasOwnProperty(e))return DatasetsNamespace[e](t)}static datasets(e){return new Datasets(e)}static data(e){return new datasets_Data(e)}}const Vn=hn.localeSet.id;class CalendarSymbols extends XFAObject{constructor(e){super(Vn,"calendarSymbols",!0);this.name="gregorian";this.dayNames=new XFAObjectArray(2);this.eraNames=null;this.meridiemNames=null;this.monthNames=new XFAObjectArray(2)}}class CurrencySymbol extends StringObject{constructor(e){super(Vn,"currencySymbol");this.name=getStringOption(e.name,["symbol","isoname","decimal"])}}class CurrencySymbols extends XFAObject{constructor(e){super(Vn,"currencySymbols",!0);this.currencySymbol=new XFAObjectArray(3)}}class DatePattern extends StringObject{constructor(e){super(Vn,"datePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class DatePatterns extends XFAObject{constructor(e){super(Vn,"datePatterns",!0);this.datePattern=new XFAObjectArray(4)}}class DateTimeSymbols extends ContentObject{constructor(e){super(Vn,"dateTimeSymbols")}}class Day extends StringObject{constructor(e){super(Vn,"day")}}class DayNames extends XFAObject{constructor(e){super(Vn,"dayNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.day=new XFAObjectArray(7)}}class Era extends StringObject{constructor(e){super(Vn,"era")}}class EraNames extends XFAObject{constructor(e){super(Vn,"eraNames",!0);this.era=new XFAObjectArray(2)}}class locale_set_Locale extends XFAObject{constructor(e){super(Vn,"locale",!0);this.desc=e.desc||"";this.name="isoname";this.calendarSymbols=null;this.currencySymbols=null;this.datePatterns=null;this.dateTimeSymbols=null;this.numberPatterns=null;this.numberSymbols=null;this.timePatterns=null;this.typeFaces=null}}class locale_set_LocaleSet extends XFAObject{constructor(e){super(Vn,"localeSet",!0);this.locale=new XFAObjectArray}}class Meridiem extends StringObject{constructor(e){super(Vn,"meridiem")}}class MeridiemNames extends XFAObject{constructor(e){super(Vn,"meridiemNames",!0);this.meridiem=new XFAObjectArray(2)}}class Month extends StringObject{constructor(e){super(Vn,"month")}}class MonthNames extends XFAObject{constructor(e){super(Vn,"monthNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.month=new XFAObjectArray(12)}}class NumberPattern extends StringObject{constructor(e){super(Vn,"numberPattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class NumberPatterns extends XFAObject{constructor(e){super(Vn,"numberPatterns",!0);this.numberPattern=new XFAObjectArray(4)}}class NumberSymbol extends StringObject{constructor(e){super(Vn,"numberSymbol");this.name=getStringOption(e.name,["decimal","grouping","percent","minus","zero"])}}class NumberSymbols extends XFAObject{constructor(e){super(Vn,"numberSymbols",!0);this.numberSymbol=new XFAObjectArray(5)}}class TimePattern extends StringObject{constructor(e){super(Vn,"timePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class TimePatterns extends XFAObject{constructor(e){super(Vn,"timePatterns",!0);this.timePattern=new XFAObjectArray(4)}}class TypeFace extends XFAObject{constructor(e){super(Vn,"typeFace",!0);this.name=""|e.name}}class TypeFaces extends XFAObject{constructor(e){super(Vn,"typeFaces",!0);this.typeFace=new XFAObjectArray}}class LocaleSetNamespace{static[Cn](e,t){if(LocaleSetNamespace.hasOwnProperty(e))return LocaleSetNamespace[e](t)}static calendarSymbols(e){return new CalendarSymbols(e)}static currencySymbol(e){return new CurrencySymbol(e)}static currencySymbols(e){return new CurrencySymbols(e)}static datePattern(e){return new DatePattern(e)}static datePatterns(e){return new DatePatterns(e)}static dateTimeSymbols(e){return new DateTimeSymbols(e)}static day(e){return new Day(e)}static dayNames(e){return new DayNames(e)}static era(e){return new Era(e)}static eraNames(e){return new EraNames(e)}static locale(e){return new locale_set_Locale(e)}static localeSet(e){return new locale_set_LocaleSet(e)}static meridiem(e){return new Meridiem(e)}static meridiemNames(e){return new MeridiemNames(e)}static month(e){return new Month(e)}static monthNames(e){return new MonthNames(e)}static numberPattern(e){return new NumberPattern(e)}static numberPatterns(e){return new NumberPatterns(e)}static numberSymbol(e){return new NumberSymbol(e)}static numberSymbols(e){return new NumberSymbols(e)}static timePattern(e){return new TimePattern(e)}static timePatterns(e){return new TimePatterns(e)}static typeFace(e){return new TypeFace(e)}static typeFaces(e){return new TypeFaces(e)}}const zn=hn.signature.id;class signature_Signature extends XFAObject{constructor(e){super(zn,"signature",!0)}}class SignatureNamespace{static[Cn](e,t){if(SignatureNamespace.hasOwnProperty(e))return SignatureNamespace[e](t)}static signature(e){return new signature_Signature(e)}}const _n=hn.stylesheet.id;class Stylesheet extends XFAObject{constructor(e){super(_n,"stylesheet",!0)}}class StylesheetNamespace{static[Cn](e,t){if(StylesheetNamespace.hasOwnProperty(e))return StylesheetNamespace[e](t)}static stylesheet(e){return new Stylesheet(e)}}const $n=hn.xdp.id;class xdp_Xdp extends XFAObject{constructor(e){super($n,"xdp",!0);this.uuid=e.uuid||"";this.timeStamp=e.timeStamp||"";this.config=null;this.connectionSet=null;this.datasets=null;this.localeSet=null;this.stylesheet=new XFAObjectArray;this.template=null}[Wr](e){const t=hn[e[qr]];return t&&e[Tr]===t.id}}class XdpNamespace{static[Cn](e,t){if(XdpNamespace.hasOwnProperty(e))return XdpNamespace[e](t)}static xdp(e){return new xdp_Xdp(e)}}const Ag=hn.xhtml.id,eg=Symbol(),tg=new Set(["color","font","font-family","font-size","font-stretch","font-style","font-weight","margin","margin-bottom","margin-left","margin-right","margin-top","letter-spacing","line-height","orphans","page-break-after","page-break-before","page-break-inside","tab-interval","tab-stop","text-align","text-decoration","text-indent","vertical-align","widows","kerning-mode","xfa-font-horizontal-scale","xfa-font-vertical-scale","xfa-spacerun","xfa-tab-stops"]),ig=new Map([["page-break-after","breakAfter"],["page-break-before","breakBefore"],["page-break-inside","breakInside"],["kerning-mode",e=>"none"===e?"none":"normal"],["xfa-font-horizontal-scale",e=>`scaleX(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-font-vertical-scale",e=>`scaleY(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-spacerun",""],["xfa-tab-stops",""],["font-size",(e,t)=>measureToString(.99*(e=t.fontSize=getMeasurement(e)))],["letter-spacing",e=>measureToString(getMeasurement(e))],["line-height",e=>measureToString(getMeasurement(e))],["margin",e=>measureToString(getMeasurement(e))],["margin-bottom",e=>measureToString(getMeasurement(e))],["margin-left",e=>measureToString(getMeasurement(e))],["margin-right",e=>measureToString(getMeasurement(e))],["margin-top",e=>measureToString(getMeasurement(e))],["text-indent",e=>measureToString(getMeasurement(e))],["font-family",e=>e],["vertical-align",e=>measureToString(getMeasurement(e))]]),ag=/\s+/g,sg=/[\r\n]+/g,rg=/\r\n?/g;function mapStyle(e,t,i){const a=Object.create(null);if(!e)return a;const s=Object.create(null);for(const[t,i]of e.split(";").map((e=>e.split(":",2)))){const e=ig.get(t);if(""===e)continue;let r=i;e&&(r="string"==typeof e?e:e(i,s));t.endsWith("scale")?a.transform=a.transform?`${a[t]} ${r}`:r:a[t.replaceAll(/-([a-zA-Z])/g,((e,t)=>t.toUpperCase()))]=r}a.fontFamily&&setFontFamily({typeface:a.fontFamily,weight:a.fontWeight||"normal",posture:a.fontStyle||"normal",size:s.fontSize||0},t,t[Fr].fontFinder,a);if(i&&a.verticalAlign&&"0px"!==a.verticalAlign&&a.fontSize){const e=.583,t=.333,i=getMeasurement(a.fontSize);a.fontSize=measureToString(i*e);a.verticalAlign=measureToString(Math.sign(getMeasurement(a.verticalAlign))*i*t)}i&&a.fontSize&&(a.fontSize=`calc(${a.fontSize} * var(--scale-factor))`);fixTextIndent(a);return a}const ng=new Set(["body","html"]);class XhtmlObject extends XmlObject{constructor(e,t){super(Ag,t);this[eg]=!1;this.style=e.style||""}[Ar](e){super[Ar](e);this.style=function checkStyle(e){return e.style?e.style.trim().split(/\s*;\s*/).filter((e=>!!e)).map((e=>e.split(/\s*:\s*/,2))).filter((([t,i])=>{"font-family"===t&&e[Fr].usedTypefaces.add(i);return tg.has(t)})).map((e=>e.join(":"))).join(";"):""}(this)}[Vs](){return!ng.has(this[qr])}[jr](e,t=!1){if(t)this[eg]=!0;else{e=e.replaceAll(sg,"");this.style.includes("xfa-spacerun:yes")||(e=e.replaceAll(ag," "))}e&&(this[sr]+=e)}[Xr](e,t=!0){const i=Object.create(null),a={top:NaN,bottom:NaN,left:NaN,right:NaN};let s=null;for(const[e,t]of this.style.split(";").map((e=>e.split(":",2))))switch(e){case"font-family":i.typeface=stripQuotes(t);break;case"font-size":i.size=getMeasurement(t);break;case"font-weight":i.weight=t;break;case"font-style":i.posture=t;break;case"letter-spacing":i.letterSpacing=getMeasurement(t);break;case"margin":const e=t.split(/ \t/).map((e=>getMeasurement(e)));switch(e.length){case 1:a.top=a.bottom=a.left=a.right=e[0];break;case 2:a.top=a.bottom=e[0];a.left=a.right=e[1];break;case 3:a.top=e[0];a.bottom=e[2];a.left=a.right=e[1];break;case 4:a.top=e[0];a.left=e[1];a.bottom=e[2];a.right=e[3]}break;case"margin-top":a.top=getMeasurement(t);break;case"margin-bottom":a.bottom=getMeasurement(t);break;case"margin-left":a.left=getMeasurement(t);break;case"margin-right":a.right=getMeasurement(t);break;case"line-height":s=getMeasurement(t)}e.pushData(i,a,s);if(this[sr])e.addString(this[sr]);else for(const t of this[pr]())"#text"!==t[qr]?t[Xr](e):e.addString(t[sr]);t&&e.popFont()}[gn](e){const t=[];this[gr]={children:t};this[$s]({});if(0===t.length&&!this[sr])return HTMLResult.EMPTY;let i;i=this[eg]?this[sr]?this[sr].replaceAll(rg,"\n"):void 0:this[sr]||void 0;return HTMLResult.success({name:this[qr],attributes:{href:this.href,style:mapStyle(this.style,this,this[eg])},children:t,value:i})}}class A extends XhtmlObject{constructor(e){super(e,"a");this.href=fixURL(e.href)||""}}class B extends XhtmlObject{constructor(e){super(e,"b")}[Xr](e){e.pushFont({weight:"bold"});super[Xr](e);e.popFont()}}class Body extends XhtmlObject{constructor(e){super(e,"body")}[gn](e){const t=super[gn](e),{html:i}=t;if(!i)return HTMLResult.EMPTY;i.name="div";i.attributes.class=["xfaRich"];return t}}class Br extends XhtmlObject{constructor(e){super(e,"br")}[rn](){return"\n"}[Xr](e){e.addString("\n")}[gn](e){return HTMLResult.success({name:"br"})}}class Html extends XhtmlObject{constructor(e){super(e,"html")}[gn](e){const t=[];this[gr]={children:t};this[$s]({});if(0===t.length)return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},value:this[sr]||""});if(1===t.length){const e=t[0];if(e.attributes?.class.includes("xfaRich"))return HTMLResult.success(e)}return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},children:t})}}class I extends XhtmlObject{constructor(e){super(e,"i")}[Xr](e){e.pushFont({posture:"italic"});super[Xr](e);e.popFont()}}class Li extends XhtmlObject{constructor(e){super(e,"li")}}class Ol extends XhtmlObject{constructor(e){super(e,"ol")}}class P extends XhtmlObject{constructor(e){super(e,"p")}[Xr](e){super[Xr](e,!1);e.addString("\n");e.addPara();e.popFont()}[rn](){return this[Dr]()[pr]().at(-1)===this?super[rn]():super[rn]()+"\n"}}class Span extends XhtmlObject{constructor(e){super(e,"span")}}class Sub extends XhtmlObject{constructor(e){super(e,"sub")}}class Sup extends XhtmlObject{constructor(e){super(e,"sup")}}class Ul extends XhtmlObject{constructor(e){super(e,"ul")}}class XhtmlNamespace{static[Cn](e,t){if(XhtmlNamespace.hasOwnProperty(e))return XhtmlNamespace[e](t)}static a(e){return new A(e)}static b(e){return new B(e)}static body(e){return new Body(e)}static br(e){return new Br(e)}static html(e){return new Html(e)}static i(e){return new I(e)}static li(e){return new Li(e)}static ol(e){return new Ol(e)}static p(e){return new P(e)}static span(e){return new Span(e)}static sub(e){return new Sub(e)}static sup(e){return new Sup(e)}static ul(e){return new Ul(e)}}const gg={config:ConfigNamespace,connection:ConnectionSetNamespace,datasets:DatasetsNamespace,localeSet:LocaleSetNamespace,signature:SignatureNamespace,stylesheet:StylesheetNamespace,template:TemplateNamespace,xdp:XdpNamespace,xhtml:XhtmlNamespace};class UnknownNamespace{constructor(e){this.namespaceId=e}[Cn](e,t){return new XmlObject(this.namespaceId,e,t)}}class Root extends XFAObject{constructor(e){super(-1,"root",Object.create(null));this.element=null;this[kr]=e}[Pr](e){this.element=e;return!0}[or](){super[or]();if(this.element.template instanceof Template){this[kr].set(_r,this.element);this.element.template[$r](this[kr]);this.element.template[kr]=this[kr]}}}class Empty extends XFAObject{constructor(){super(-1,"",Object.create(null))}[Pr](e){return!1}}class Builder{constructor(e=null){this._namespaceStack=[];this._nsAgnosticLevel=0;this._namespacePrefixes=new Map;this._namespaces=new Map;this._nextNsId=Math.max(...Object.values(hn).map((({id:e})=>e)));this._currentNamespace=e||new UnknownNamespace(++this._nextNsId)}buildRoot(e){return new Root(e)}build({nsPrefix:e,name:t,attributes:i,namespace:a,prefixes:s}){const r=null!==a;if(r){this._namespaceStack.push(this._currentNamespace);this._currentNamespace=this._searchNamespace(a)}s&&this._addNamespacePrefix(s);if(i.hasOwnProperty(Or)){const e=gg.datasets,t=i[Or];let a=null;for(const[i,s]of Object.entries(t)){if(this._getNamespaceToUse(i)===e){a={xfa:s};break}}a?i[Or]=a:delete i[Or]}const n=this._getNamespaceToUse(e),g=n?.[Cn](t,i)||new Empty;g[Lr]()&&this._nsAgnosticLevel++;(r||s||g[Lr]())&&(g[tr]={hasNamespace:r,prefixes:s,nsAgnostic:g[Lr]()});return g}isNsAgnostic(){return this._nsAgnosticLevel>0}_searchNamespace(e){let t=this._namespaces.get(e);if(t)return t;for(const[i,{check:a}]of Object.entries(hn))if(a(e)){t=gg[i];if(t){this._namespaces.set(e,t);return t}break}t=new UnknownNamespace(++this._nextNsId);this._namespaces.set(e,t);return t}_addNamespacePrefix(e){for(const{prefix:t,value:i}of e){const e=this._searchNamespace(i);let a=this._namespacePrefixes.get(t);if(!a){a=[];this._namespacePrefixes.set(t,a)}a.push(e)}}_getNamespaceToUse(e){if(!e)return this._currentNamespace;const t=this._namespacePrefixes.get(e);if(t?.length>0)return t.at(-1);warn(`Unknown namespace prefix: ${e}.`);return null}clean(e){const{hasNamespace:t,prefixes:i,nsAgnostic:a}=e;t&&(this._currentNamespace=this._namespaceStack.pop());i&&i.forEach((({prefix:e})=>{this._namespacePrefixes.get(e).pop()}));a&&this._nsAgnosticLevel--}}class XFAParser extends XMLParserBase{constructor(e=null,t=!1){super();this._builder=new Builder(e);this._stack=[];this._globalData={usedTypefaces:new Set};this._ids=new Map;this._current=this._builder.buildRoot(this._ids);this._errorCode=Us;this._whiteRegex=/^\s+$/;this._nbsps=/\xa0+/g;this._richText=t}parse(e){this.parseXml(e);if(this._errorCode===Us){this._current[or]();return this._current.element}}onText(e){e=e.replace(this._nbsps,(e=>e.slice(1)+" "));this._richText||this._current[Vs]()?this._current[jr](e,this._richText):this._whiteRegex.test(e)||this._current[jr](e.trim())}onCdata(e){this._current[jr](e)}_mkAttributes(e,t){let i=null,a=null;const s=Object.create({});for(const{name:r,value:n}of e)if("xmlns"===r)i?warn(`XFA - multiple namespace definition in <${t}>`):i=n;else if(r.startsWith("xmlns:")){const e=r.substring(6);a||(a=[]);a.push({prefix:e,value:n})}else{const e=r.indexOf(":");if(-1===e)s[r]=n;else{let t=s[Or];t||(t=s[Or]=Object.create(null));const[i,a]=[r.slice(0,e),r.slice(e+1)];(t[i]||=Object.create(null))[a]=n}}return[i,a,s]}_getNameAndPrefix(e,t){const i=e.indexOf(":");return-1===i?[e,null]:[e.substring(i+1),t?"":e.substring(0,i)]}onBeginElement(e,t,i){const[a,s,r]=this._mkAttributes(t,e),[n,g]=this._getNameAndPrefix(e,this._builder.isNsAgnostic()),o=this._builder.build({nsPrefix:g,name:n,attributes:r,namespace:a,prefixes:s});o[Fr]=this._globalData;if(i){o[or]();this._current[Pr](o)&&o[en](this._ids);o[Ar](this._builder)}else{this._stack.push(this._current);this._current=o}}onEndElement(e){const t=this._current;if(t[Gr]()&&"string"==typeof t[sr]){const e=new XFAParser;e._globalData=this._globalData;const i=e.parse(t[sr]);t[sr]=null;t[Pr](i)}t[or]();this._current=this._stack.pop();this._current[Pr](t)&&t[en](this._ids);t[Ar](this._builder)}onError(e){this._errorCode=e}}class XFAFactory{constructor(e){try{this.root=(new XFAParser).parse(XFAFactory._createDocument(e));const t=new Binder(this.root);this.form=t.bind();this.dataHandler=new DataHandler(this.root,t.getData());this.form[Fr].template=this.form}catch(e){warn(`XFA - an error occurred during parsing and binding: ${e}`)}}isValid(){return this.root&&this.form}_createPagesHelper(){const e=this.form[nn]();return new Promise(((t,i)=>{const nextIteration=()=>{try{const i=e.next();i.done?t(i.value):setTimeout(nextIteration,0)}catch(e){i(e)}};setTimeout(nextIteration,0)}))}async _createPages(){try{this.pages=await this._createPagesHelper();this.dims=this.pages.children.map((e=>{const{width:t,height:i}=e.attributes.style;return[0,0,parseInt(t),parseInt(i)]}))}catch(e){warn(`XFA - an error occurred during layout: ${e}`)}}getBoundingBox(e){return this.dims[e]}async getNumPages(){this.pages||await this._createPages();return this.dims.length}setImages(e){this.form[Fr].images=e}setFonts(e){this.form[Fr].fontFinder=new FontFinder(e);const t=[];for(let e of this.form[Fr].usedTypefaces){e=stripQuotes(e);this.form[Fr].fontFinder.find(e)||t.push(e)}return t.length>0?t:null}appendFonts(e,t){this.form[Fr].fontFinder.add(e,t)}async getPages(){this.pages||await this._createPages();const e=this.pages;this.pages=null;return e}serializeData(e){return this.dataHandler.serialize(e)}static _createDocument(e){return e["/xdp:xdp"]?Object.values(e).join(""):e["xdp:xdp"]}static getRichTextAsHtml(e){if(!e||"string"!=typeof e)return null;try{let t=new XFAParser(XhtmlNamespace,!0).parse(e);if(!["body","xhtml"].includes(t[qr])){const e=XhtmlNamespace.body({});e[_s](t);t=e}const i=t[gn]();if(!i.success)return null;const{html:a}=i,{attributes:s}=a;if(s){s.class&&(s.class=s.class.filter((e=>!e.startsWith("xfa"))));s.dir="auto"}return{html:a,str:t[rn]()}}catch(e){warn(`XFA - an error occurred during parsing of rich text: ${e}`)}return null}}class AnnotationFactory{static createGlobals(e){return Promise.all([e.ensureCatalog("acroForm"),e.ensureDoc("xfaDatasets"),e.ensureCatalog("structTreeRoot"),e.ensureCatalog("baseUrl"),e.ensureCatalog("attachments")]).then((([t,i,a,s,r])=>({pdfManager:e,acroForm:t instanceof Dict?t:Dict.empty,xfaDatasets:i,structTreeRoot:a,baseUrl:s,attachments:r})),(e=>{warn(`createGlobals: "${e}".`);return null}))}static async create(e,t,i,a,s,r,n){const g=s?await this._getPageIndex(e,t,i.pdfManager):null;return i.pdfManager.ensure(this,"_create",[e,t,i,a,s,r,g,n])}static _create(e,t,i,a,s=!1,r=null,n=null,g=null){const o=e.fetchIfRef(t);if(!(o instanceof Dict))return;const{acroForm:c,pdfManager:C}=i,h=t instanceof Ref?t.toString():`annot_${a.createObjId()}`;let l=o.get("Subtype");l=l instanceof Name?l.name:null;const Q={xref:e,ref:t,dict:o,subtype:l,id:h,annotationGlobals:i,collectFields:s,orphanFields:r,needAppearances:!s&&!0===c.get("NeedAppearances"),pageIndex:n,evaluatorOptions:C.evaluatorOptions,pageRef:g};switch(l){case"Link":return new LinkAnnotation(Q);case"Text":return new TextAnnotation(Q);case"Widget":let e=getInheritableProperty({dict:o,key:"FT"});e=e instanceof Name?e.name:null;switch(e){case"Tx":return new TextWidgetAnnotation(Q);case"Btn":return new ButtonWidgetAnnotation(Q);case"Ch":return new ChoiceWidgetAnnotation(Q);case"Sig":return new SignatureWidgetAnnotation(Q)}warn(`Unimplemented widget field type "${e}", falling back to base field type.`);return new WidgetAnnotation(Q);case"Popup":return new PopupAnnotation(Q);case"FreeText":return new FreeTextAnnotation(Q);case"Line":return new LineAnnotation(Q);case"Square":return new SquareAnnotation(Q);case"Circle":return new CircleAnnotation(Q);case"PolyLine":return new PolylineAnnotation(Q);case"Polygon":return new PolygonAnnotation(Q);case"Caret":return new CaretAnnotation(Q);case"Ink":return new InkAnnotation(Q);case"Highlight":return new HighlightAnnotation(Q);case"Underline":return new UnderlineAnnotation(Q);case"Squiggly":return new SquigglyAnnotation(Q);case"StrikeOut":return new StrikeOutAnnotation(Q);case"Stamp":return new StampAnnotation(Q);case"FileAttachment":return new FileAttachmentAnnotation(Q);default:s||warn(l?`Unimplemented annotation type "${l}", falling back to base annotation.`:"Annotation is missing the required /Subtype.");return new Annotation(Q)}}static async _getPageIndex(e,t,i){try{const a=await e.fetchIfRefAsync(t);if(!(a instanceof Dict))return-1;const s=a.getRaw("P");if(s instanceof Ref)try{return await i.ensureCatalog("getPageIndex",[s])}catch(e){info(`_getPageIndex -- not a valid page reference: "${e}".`)}if(a.has("Kids"))return-1;const r=await i.ensureDoc("numPages");for(let e=0;e<r;e++){const a=await i.getPage(e),s=await i.ensure(a,"annotations");for(const i of s)if(i instanceof Ref&&isRefsEqual(i,t))return e}}catch(e){warn(`_getPageIndex: "${e}".`)}return-1}static generateImages(e,t,i){if(!i){warn("generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images.");return null}let a;for(const{bitmapId:i,bitmap:s}of e)if(s){a||=new Map;a.set(i,StampAnnotation.createImage(s,t))}return a}static async saveNewAnnotations(e,t,i,a){const s=e.xref;let r;const n=[],g=[],{isOffscreenCanvasSupported:o}=e.options;for(const c of i)if(!c.deleted)switch(c.annotationType){case d:if(!r){const e=new Dict(s);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));const t=[];r=s.getNewTemporaryRef();await writeObject(r,e,t,s);n.push({ref:r,data:t.join("")})}g.push(FreeTextAnnotation.createNewAnnotation(s,c,n,{evaluator:e,task:t,baseFontRef:r}));break;case f:c.quadPoints?g.push(HighlightAnnotation.createNewAnnotation(s,c,n)):g.push(InkAnnotation.createNewAnnotation(s,c,n));break;case m:g.push(InkAnnotation.createNewAnnotation(s,c,n));break;case p:const i=o?await(a?.get(c.bitmapId)):null;if(i?.imageStream){const{imageStream:e,smaskStream:t}=i,a=[];if(t){const i=s.getNewTemporaryRef();await writeObject(i,t,a,s);n.push({ref:i,data:a.join("")});e.dict.set("SMask",i);a.length=0}const r=i.imageRef=s.getNewTemporaryRef();await writeObject(r,e,a,s);n.push({ref:r,data:a.join("")});i.imageStream=i.smaskStream=null}g.push(StampAnnotation.createNewAnnotation(s,c,n,{image:i}))}return{annotations:await Promise.all(g),dependencies:n}}static async printNewAnnotations(e,t,i,a,s){if(!a)return null;const{options:r,xref:n}=t,g=[];for(const o of a)if(!o.deleted)switch(o.annotationType){case d:g.push(FreeTextAnnotation.createNewPrintAnnotation(e,n,o,{evaluator:t,task:i,evaluatorOptions:r}));break;case f:o.quadPoints?g.push(HighlightAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r})):g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case m:g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case p:const a=r.isOffscreenCanvasSupported?await(s?.get(o.bitmapId)):null;if(a?.imageStream){const{imageStream:e,smaskStream:t}=a;t&&e.dict.set("SMask",t);a.imageRef=new JpegStream(e,e.length);a.imageStream=a.smaskStream=null}g.push(StampAnnotation.createNewPrintAnnotation(e,n,o,{image:a,evaluatorOptions:r}))}return Promise.all(g)}}function getRgbColor(e,t=new Uint8ClampedArray(3)){if(!Array.isArray(e))return t;const i=t||new Uint8ClampedArray(3);switch(e.length){case 0:return null;case 1:ColorSpace.singletons.gray.getRgbItem(e,0,i,0);return i;case 3:ColorSpace.singletons.rgb.getRgbItem(e,0,i,0);return i;case 4:ColorSpace.singletons.cmyk.getRgbItem(e,0,i,0);return i;default:return t}}function getPdfColorArray(e){return Array.from(e,(e=>e/255))}function getQuadPoints(e,t){const i=e.getArray("QuadPoints");if(!isNumberArray(i,null)||0===i.length||i.length%8>0)return null;const a=new Float32Array(i.length);for(let e=0,s=i.length;e<s;e+=8){const[s,r,n,g,o,c,C,h]=i.slice(e,e+8),l=Math.min(s,n,o,C),Q=Math.max(s,n,o,C),E=Math.min(r,g,c,h),u=Math.max(r,g,c,h);if(null!==t&&(l<t[0]||Q>t[2]||E<t[1]||u>t[3]))return null;a.set([l,u,Q,u,l,E,Q,E],e)}return a}function getTransformMatrix(e,t,i){const[a,s,r,n]=Util.getAxialAlignedBoundingBox(t,i);if(a===r||s===n)return[1,0,0,1,e[0],e[1]];const g=(e[2]-e[0])/(r-a),o=(e[3]-e[1])/(n-s);return[g,0,0,o,e[0]-a*g,e[1]-s*o]}class Annotation{constructor(e){const{dict:t,xref:i,annotationGlobals:a,ref:s,orphanFields:r}=e,n=r?.get(s);n&&t.set("Parent",n);this.setTitle(t.get("T"));this.setContents(t.get("Contents"));this.setModificationDate(t.get("M"));this.setFlags(t.get("F"));this.setRectangle(t.getArray("Rect"));this.setColor(t.getArray("C"));this.setBorderStyle(t);this.setAppearance(t);this.setOptionalContent(t);const g=t.get("MK");this.setBorderAndBackgroundColors(g);this.setRotation(g,t);this.ref=e.ref instanceof Ref?e.ref:null;this._streams=[];this.appearance&&this._streams.push(this.appearance);const o=!!(this.flags&eA),c=!!(this.flags&tA);this.data={annotationFlags:this.flags,borderStyle:this.borderStyle,color:this.color,backgroundColor:this.backgroundColor,borderColor:this.borderColor,rotation:this.rotation,contentsObj:this._contents,hasAppearance:!!this.appearance,id:e.id,modificationDate:this.modificationDate,rect:this.rectangle,subtype:e.subtype,hasOwnCanvas:!1,noRotate:!!(this.flags&$),noHTML:o&&c,isEditable:!1,structParent:-1};if(a.structTreeRoot){let i=t.get("StructParent");this.data.structParent=i=Number.isInteger(i)&&i>=0?i:-1;a.structTreeRoot.addAnnotationIdToPage(e.pageRef,i)}if(e.collectFields){const a=t.get("Kids");if(Array.isArray(a)){const e=[];for(const t of a)t instanceof Ref&&e.push(t.toString());0!==e.length&&(this.data.kidIds=e)}this.data.actions=collectActions(i,t,dA);this.data.fieldName=this._constructFieldName(t);this.data.pageIndex=e.pageIndex}const C=t.get("IT");C instanceof Name&&(this.data.it=C.name);this._isOffscreenCanvasSupported=e.evaluatorOptions.isOffscreenCanvasSupported;this._fallbackFontDict=null;this._needAppearances=!1}_hasFlag(e,t){return!!(e&t)}_buildFlags(e,t){let{flags:i}=this;if(void 0===e){if(void 0===t)return;return t?i&~_:i&~z|_}if(e){i|=_;return t?i&~AA|z:i&~z|AA}i&=~(z|AA);return t?i&~_:i|_}_isViewable(e){return!this._hasFlag(e,V)&&!this._hasFlag(e,AA)}_isPrintable(e){return this._hasFlag(e,_)&&!this._hasFlag(e,z)&&!this._hasFlag(e,V)}mustBeViewed(e,t){const i=e?.get(this.data.id)?.noView;return void 0!==i?!i:this.viewable&&!this._hasFlag(this.flags,z)}mustBePrinted(e){const t=e?.get(this.data.id)?.noPrint;return void 0!==t?!t:this.printable}mustBeViewedWhenEditing(e,t=null){return e?!this.data.isEditable:!t?.has(this.data.id)}get viewable(){return null!==this.data.quadPoints&&(0===this.flags||this._isViewable(this.flags))}get printable(){return null!==this.data.quadPoints&&(0!==this.flags&&this._isPrintable(this.flags))}_parseStringHelper(e){const t="string"==typeof e?stringToPDFString(e):"";return{str:t,dir:t&&"rtl"===bidi(t).dir?"rtl":"ltr"}}setDefaultAppearance(e){const{dict:t,annotationGlobals:i}=e,a=getInheritableProperty({dict:t,key:"DA"})||i.acroForm.get("DA");this._defaultAppearance="string"==typeof a?a:"";this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance)}setTitle(e){this._title=this._parseStringHelper(e)}setContents(e){this._contents=this._parseStringHelper(e)}setModificationDate(e){this.modificationDate="string"==typeof e?e:null}setFlags(e){this.flags=Number.isInteger(e)&&e>0?e:0;this.flags&V&&"Annotation"!==this.constructor.name&&(this.flags^=V)}hasFlag(e){return this._hasFlag(this.flags,e)}setRectangle(e){this.rectangle=lookupNormalRect(e,[0,0,0,0])}setColor(e){this.color=getRgbColor(e)}setLineEndings(e){this.lineEndings=["None","None"];if(Array.isArray(e)&&2===e.length)for(let t=0;t<2;t++){const i=e[t];if(i instanceof Name)switch(i.name){case"None":continue;case"Square":case"Circle":case"Diamond":case"OpenArrow":case"ClosedArrow":case"Butt":case"ROpenArrow":case"RClosedArrow":case"Slash":this.lineEndings[t]=i.name;continue}warn(`Ignoring invalid lineEnding: ${i}`)}}setRotation(e,t){this.rotation=0;let i=e instanceof Dict?e.get("R")||0:t.get("Rotate")||0;if(Number.isInteger(i)&&0!==i){i%=360;i<0&&(i+=360);i%90==0&&(this.rotation=i)}}setBorderAndBackgroundColors(e){if(e instanceof Dict){this.borderColor=getRgbColor(e.getArray("BC"),null);this.backgroundColor=getRgbColor(e.getArray("BG"),null)}else this.borderColor=this.backgroundColor=null}setBorderStyle(e){this.borderStyle=new AnnotationBorderStyle;if(e instanceof Dict)if(e.has("BS")){const t=e.get("BS");if(t instanceof Dict){const e=t.get("Type");if(!e||isName(e,"Border")){this.borderStyle.setWidth(t.get("W"),this.rectangle);this.borderStyle.setStyle(t.get("S"));this.borderStyle.setDashArray(t.getArray("D"))}}}else if(e.has("Border")){const t=e.getArray("Border");if(Array.isArray(t)&&t.length>=3){this.borderStyle.setHorizontalCornerRadius(t[0]);this.borderStyle.setVerticalCornerRadius(t[1]);this.borderStyle.setWidth(t[2],this.rectangle);4===t.length&&this.borderStyle.setDashArray(t[3],!0)}}else this.borderStyle.setWidth(0)}setAppearance(e){this.appearance=null;const t=e.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(i instanceof BaseStream){this.appearance=i;return}if(!(i instanceof Dict))return;const a=e.get("AS");if(!(a instanceof Name&&i.has(a.name)))return;const s=i.get(a.name);s instanceof BaseStream&&(this.appearance=s)}setOptionalContent(e){this.oc=null;const t=e.get("OC");t instanceof Name?warn("setOptionalContent: Support for /Name-entry is not implemented."):t instanceof Dict&&(this.oc=t)}loadResources(e,t){return t.dict.getAsync("Resources").then((t=>{if(!t)return;return new ObjectLoader(t,e,t.xref).load().then((function(){return t}))}))}async getOperatorList(e,t,a,s){const{hasOwnCanvas:r,id:n,rect:g}=this.data;let c=this.appearance;const C=!!(r&&a&o);if(C&&(g[0]===g[2]||g[1]===g[3])){this.data.hasOwnCanvas=!1;return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}if(!c){if(!C)return{opList:new OperatorList,separateForm:!1,separateCanvas:!1};c=new StringStream("");c.dict=new Dict}const h=c.dict,l=await this.loadResources(["ExtGState","ColorSpace","Pattern","Shading","XObject","Font"],c),Q=lookupRect(h.getArray("BBox"),[0,0,1,1]),E=lookupMatrix(h.getArray("Matrix"),i),u=getTransformMatrix(g,Q,E),d=new OperatorList;let f;this.oc&&(f=await e.parseMarkedContentProps(this.oc,null));void 0!==f&&d.addOp(Ye,["OC",f]);d.addOp(je,[n,g,u,E,C]);await e.getOperatorList({stream:c,task:t,resources:l,operatorList:d,fallbackFontDict:this._fallbackFontDict});d.addOp(Xe,[]);void 0!==f&&d.addOp(ve,[]);this.reset();return{opList:d,separateForm:!1,separateCanvas:C}}async save(e,t,i){return null}get hasTextContent(){return!1}async extractTextContent(e,t,i){if(!this.appearance)return;const a=await this.loadResources(["ExtGState","Font","Properties","XObject"],this.appearance),s=[],r=[];let n=null;const g={desiredSize:Math.Infinity,ready:!0,enqueue(e,t){for(const t of e.items)if(void 0!==t.str){n||=t.transform.slice(-2);r.push(t.str);if(t.hasEOL){s.push(r.join("").trimEnd());r.length=0}}}};await e.getTextContent({stream:this.appearance,task:t,resources:a,includeMarkedContent:!0,keepWhiteSpace:!0,sink:g,viewBox:i});this.reset();r.length&&s.push(r.join("").trimEnd());if(s.length>1||s[0]){const e=this.appearance.dict,t=lookupRect(e.getArray("BBox"),null),i=lookupMatrix(e.getArray("Matrix"),null);this.data.textPosition=this._transformPoint(n,t,i);this.data.textContent=s}}_transformPoint(e,t,i){const{rect:a}=this.data;t||=[0,0,1,1];i||=[1,0,0,1,0,0];const s=getTransformMatrix(a,t,i);s[4]-=a[0];s[5]-=a[1];e=Util.applyTransform(e,s);return Util.applyTransform(e,i)}getFieldObject(){return this.data.kidIds?{id:this.data.id,actions:this.data.actions,name:this.data.fieldName,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,type:"",kidIds:this.data.kidIds,page:this.data.pageIndex,rotation:this.rotation}:null}reset(){for(const e of this._streams)e.reset()}_constructFieldName(e){if(!e.has("T")&&!e.has("Parent")){warn("Unknown field name, falling back to empty field name.");return""}if(!e.has("Parent"))return stringToPDFString(e.get("T"));const t=[];e.has("T")&&t.unshift(stringToPDFString(e.get("T")));let i=e;const a=new RefSet;e.objId&&a.put(e.objId);for(;i.has("Parent");){i=i.get("Parent");if(!(i instanceof Dict)||i.objId&&a.has(i.objId))break;i.objId&&a.put(i.objId);i.has("T")&&t.unshift(stringToPDFString(i.get("T")))}return t.join(".")}}class AnnotationBorderStyle{constructor(){this.width=1;this.rawWidth=1;this.style=BA;this.dashArray=[3];this.horizontalCornerRadius=0;this.verticalCornerRadius=0}setWidth(e,t=[0,0,0,0]){if(e instanceof Name)this.width=0;else if("number"==typeof e){if(e>0){this.rawWidth=e;const i=(t[2]-t[0])/2,a=(t[3]-t[1])/2;if(i>0&&a>0&&(e>i||e>a)){warn(`AnnotationBorderStyle.setWidth - ignoring width: ${e}`);e=1}}this.width=e}}setStyle(e){if(e instanceof Name)switch(e.name){case"S":this.style=BA;break;case"D":this.style=lA;break;case"B":this.style=QA;break;case"I":this.style=EA;break;case"U":this.style=uA}}setDashArray(e,t=!1){if(Array.isArray(e)){let i=!0,a=!0;for(const t of e){if(!(+t>=0)){i=!1;break}t>0&&(a=!1)}if(0===e.length||i&&!a){this.dashArray=e;t&&this.setStyle(Name.get("D"))}else this.width=0}else e&&(this.width=0)}setHorizontalCornerRadius(e){Number.isInteger(e)&&(this.horizontalCornerRadius=e)}setVerticalCornerRadius(e){Number.isInteger(e)&&(this.verticalCornerRadius=e)}}class MarkupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;if(t.has("IRT")){const e=t.getRaw("IRT");this.data.inReplyTo=e instanceof Ref?e.toString():null;const i=t.get("RT");this.data.replyType=i instanceof Name?i.name:Z}let i=null;if(this.data.replyType===X){const e=t.get("IRT");this.setTitle(e.get("T"));this.data.titleObj=this._title;this.setContents(e.get("Contents"));this.data.contentsObj=this._contents;if(e.has("CreationDate")){this.setCreationDate(e.get("CreationDate"));this.data.creationDate=this.creationDate}else this.data.creationDate=null;if(e.has("M")){this.setModificationDate(e.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;i=e.getRaw("Popup");if(e.has("C")){this.setColor(e.getArray("C"));this.data.color=this.color}else this.data.color=null}else{this.data.titleObj=this._title;this.setCreationDate(t.get("CreationDate"));this.data.creationDate=this.creationDate;i=t.getRaw("Popup");t.has("C")||(this.data.color=null)}this.data.popupRef=i instanceof Ref?i.toString():null;t.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(t.get("RC")))}setCreationDate(e){this.creationDate="string"==typeof e?e:null}_setDefaultAppearance({xref:e,extra:t,strokeColor:i,fillColor:a,blendMode:s,strokeAlpha:r,fillAlpha:n,pointsCallback:g}){let o=Number.MAX_VALUE,c=Number.MAX_VALUE,C=Number.MIN_VALUE,h=Number.MIN_VALUE;const l=["q"];t&&l.push(t);i&&l.push(`${i[0]} ${i[1]} ${i[2]} RG`);a&&l.push(`${a[0]} ${a[1]} ${a[2]} rg`);let Q=this.data.quadPoints;Q||(Q=Float32Array.from([this.rectangle[0],this.rectangle[3],this.rectangle[2],this.rectangle[3],this.rectangle[0],this.rectangle[1],this.rectangle[2],this.rectangle[1]]));for(let e=0,t=Q.length;e<t;e+=8){const[t,i,a,s]=g(l,Q.subarray(e,e+8));o=Math.min(o,t);C=Math.max(C,i);c=Math.min(c,a);h=Math.max(h,s)}l.push("Q");const E=new Dict(e),u=new Dict(e);u.set("Subtype",Name.get("Form"));const d=new StringStream(l.join(" "));d.dict=u;E.set("Fm0",d);const f=new Dict(e);s&&f.set("BM",Name.get(s));"number"==typeof r&&f.set("CA",r);"number"==typeof n&&f.set("ca",n);const p=new Dict(e);p.set("GS0",f);const m=new Dict(e);m.set("ExtGState",p);m.set("XObject",E);const y=new Dict(e);y.set("Resources",m);const w=this.data.rect=[o,c,C,h];y.set("BBox",w);this.appearance=new StringStream("/GS0 gs /Fm0 Do");this.appearance.dict=y;this._streams.push(this.appearance,d)}static async createNewAnnotation(e,t,i,a){t.ref||(t.ref=e.getNewTemporaryRef());const s=t.ref,r=await this.createNewAppearanceStream(t,e,a),n=[];let g;if(r){const a=e.getNewTemporaryRef();g=this.createNewDict(t,e,{apRef:a});await writeObject(a,r,n,e);i.push({ref:a,data:n.join("")})}else g=this.createNewDict(t,e,{});Number.isInteger(t.parentTreeId)&&g.set("StructParent",t.parentTreeId);n.length=0;await writeObject(s,g,n,e);return{ref:s,data:n.join("")}}static async createNewPrintAnnotation(e,t,i,a){const s=await this.createNewAppearanceStream(i,t,a),r=this.createNewDict(i,t,s?{ap:s}:{}),n=new this.prototype.constructor({dict:r,xref:t,annotationGlobals:e,evaluatorOptions:a.evaluatorOptions});i.ref&&(n.ref=n.refToReplace=i.ref);return n}}class WidgetAnnotation extends Annotation{constructor(e){super(e);const{dict:t,xref:i,annotationGlobals:a}=e,s=this.data;this._needAppearances=e.needAppearances;s.annotationType=j;void 0===s.fieldName&&(s.fieldName=this._constructFieldName(t));void 0===s.actions&&(s.actions=collectActions(i,t,dA));let r=getInheritableProperty({dict:t,key:"V",getArray:!0});s.fieldValue=this._decodeFormValue(r);const n=getInheritableProperty({dict:t,key:"DV",getArray:!0});s.defaultFieldValue=this._decodeFormValue(n);if(void 0===r&&a.xfaDatasets){const e=this._title.str;if(e){this._hasValueFromXFA=!0;s.fieldValue=r=a.xfaDatasets.getValue(e)}}void 0===r&&null!==s.defaultFieldValue&&(s.fieldValue=s.defaultFieldValue);s.alternativeText=stringToPDFString(t.get("TU")||"");this.setDefaultAppearance(e);s.hasAppearance||=this._needAppearances&&void 0!==s.fieldValue&&null!==s.fieldValue;const g=getInheritableProperty({dict:t,key:"FT"});s.fieldType=g instanceof Name?g.name:null;const o=getInheritableProperty({dict:t,key:"DR"}),c=a.acroForm.get("DR"),C=this.appearance?.dict.get("Resources");this._fieldResources={localResources:o,acroFormResources:c,appearanceResources:C,mergedResources:Dict.merge({xref:i,dictArray:[o,C,c],mergeSubDicts:!0})};s.fieldFlags=getInheritableProperty({dict:t,key:"Ff"});(!Number.isInteger(s.fieldFlags)||s.fieldFlags<0)&&(s.fieldFlags=0);s.readOnly=this.hasFieldFlag(iA);s.required=this.hasFieldFlag(aA);s.hidden=this._hasFlag(s.annotationFlags,z)||this._hasFlag(s.annotationFlags,AA)}_decodeFormValue(e){return Array.isArray(e)?e.filter((e=>"string"==typeof e)).map((e=>stringToPDFString(e))):e instanceof Name?stringToPDFString(e.name):"string"==typeof e?stringToPDFString(e):null}hasFieldFlag(e){return!!(this.data.fieldFlags&e)}_isViewable(e){return!0}mustBeViewed(e,t){return t?this.viewable:super.mustBeViewed(e,t)&&!this._hasFlag(this.flags,AA)}getRotationMatrix(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(0===t)return i;return getRotationMatrix(t,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1])}getBorderAndBackgroundAppearances(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(!this.backgroundColor&&!this.borderColor)return"";const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=0===t||180===t?`0 0 ${i} ${a} re`:`0 0 ${a} ${i} re`;let r="";this.backgroundColor&&(r=`${getPdfColor(this.backgroundColor,!0)} ${s} f `);if(this.borderColor){r+=`${this.borderStyle.width||1} w ${getPdfColor(this.borderColor,!1)} ${s} S `}return r}async getOperatorList(e,t,i,a){if(i&h&&!(this instanceof SignatureWidgetAnnotation)&&!this.data.noHTML&&!this.data.hasOwnCanvas)return{opList:new OperatorList,separateForm:!0,separateCanvas:!1};if(!this._hasText)return super.getOperatorList(e,t,i,a);const s=await this._getAppearance(e,t,i,a);if(this.appearance&&null===s)return super.getOperatorList(e,t,i,a);const r=new OperatorList;if(!this._defaultAppearance||null===s)return{opList:r,separateForm:!1,separateCanvas:!1};const n=!!(this.data.hasOwnCanvas&&i&o),g=[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]],c=getTransformMatrix(this.data.rect,g,[1,0,0,1,0,0]);let C;this.oc&&(C=await e.parseMarkedContentProps(this.oc,null));void 0!==C&&r.addOp(Ye,["OC",C]);r.addOp(je,[this.data.id,this.data.rect,c,this.getRotationMatrix(a),n]);const l=new StringStream(s);await e.getOperatorList({stream:l,task:t,resources:this._fieldResources.mergedResources,operatorList:r});r.addOp(Xe,[]);void 0!==C&&r.addOp(ve,[]);return{opList:r,separateForm:!1,separateCanvas:n}}_getMKDict(e){const t=new Dict(null);e&&t.set("R",e);this.borderColor&&t.set("BC",getPdfColorArray(this.borderColor));this.backgroundColor&&t.set("BG",getPdfColorArray(this.backgroundColor));return t.size>0?t:null}amendSavedDict(e,t){}async save(e,t,a){const s=a?.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let n=s?.value,g=s?.rotation;if(n===this.data.fieldValue||void 0===n){if(!this._hasValueFromXFA&&void 0===g&&void 0===r)return null;n||=this.data.fieldValue}if(void 0===g&&!this._hasValueFromXFA&&Array.isArray(n)&&Array.isArray(this.data.fieldValue)&&isArrayEqual(n,this.data.fieldValue)&&void 0===r)return null;void 0===g&&(g=this.rotation);let o=null;if(!this._needAppearances){o=await this._getAppearance(e,t,C,a);if(null===o&&void 0===r)return null}let c=!1;if(o?.needAppearances){c=!0;o=null}const{xref:h}=e,l=h.fetchIfRef(this.ref);if(!(l instanceof Dict))return null;const Q=new Dict(h);for(const e of l.getKeys())"AP"!==e&&Q.set(e,l.getRaw(e));if(void 0!==r){Q.set("F",r);if(null===o&&!c){const e=l.getRaw("AP");e&&Q.set("AP",e)}}const E={path:this.data.fieldName,value:n};Q.set("V",Array.isArray(n)?n.map(stringToAsciiOrUTF16BE):stringToAsciiOrUTF16BE(n));this.amendSavedDict(a,Q);const u=this._getMKDict(g);u&&Q.set("MK",u);const d=[],f=[{ref:this.ref,data:"",xfa:E,needAppearances:c}];if(null!==o){const e=h.getNewTemporaryRef(),t=new Dict(h);Q.set("AP",t);t.set("N",e);const s=this._getSaveFieldResources(h),r=new StringStream(o),n=r.dict=new Dict(h);n.set("Subtype",Name.get("Form"));n.set("Resources",s);n.set("BBox",[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]]);const g=this.getRotationMatrix(a);g!==i&&n.set("Matrix",g);await writeObject(e,r,d,h);f.push({ref:e,data:d.join(""),xfa:null,needAppearances:!1});d.length=0}Q.set("M",`D:${getModificationDate()}`);await writeObject(this.ref,Q,d,h);f[0].data=d.join("");return f}async _getAppearance(e,t,i,a){if(this.hasFieldFlag(rA))return null;const s=a?.get(this.data.id);let r,g;if(s){r=s.formattedValue||s.value;g=s.rotation}if(void 0===g&&void 0===r&&!this._needAppearances&&(!this._hasValueFromXFA||this.appearance))return null;const o=this.getBorderAndBackgroundAppearances(a);if(void 0===r){r=this.data.fieldValue;if(!r)return`/Tx BMC q ${o}Q EMC`}Array.isArray(r)&&1===r.length&&(r=r[0]);assert("string"==typeof r,"Expected `value` to be a string.");r=r.trimEnd();if(this.data.combo){const e=this.data.options.find((({exportValue:e})=>r===e));r=e?.displayValue||r}if(""===r)return`/Tx BMC q ${o}Q EMC`;void 0===g&&(g=this.rotation);let c,h=-1;if(this.data.multiLine){c=r.split(/\r\n?|\n/).map((e=>e.normalize("NFC")));h=c.length}else c=[r.replace(/\r\n?|\n/,"").normalize("NFC")];let l=this.data.rect[3]-this.data.rect[1],Q=this.data.rect[2]-this.data.rect[0];90!==g&&270!==g||([Q,l]=[l,Q]);this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));let E,u,d,f=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);const p=[];let m=!1;for(const e of c){const t=f.encodeString(e);t.length>1&&(m=!0);p.push(t.join(""))}if(m&&i&C)return{needAppearances:!0};if(m&&this._isOffscreenCanvasSupported){const i=this.data.comb?"monospace":"sans-serif",a=new FakeUnicodeFont(e.xref,i),s=a.createFontResources(c.join("")),n=s.getRaw("Font");if(this._fieldResources.mergedResources.has("Font")){const e=this._fieldResources.mergedResources.get("Font");for(const t of n.getKeys())e.set(t,n.getRaw(t))}else this._fieldResources.mergedResources.set("Font",n);const g=a.fontName.name;f=await WidgetAnnotation._getFontData(e,t,{fontName:g,fontSize:0},s);for(let e=0,t=p.length;e<t;e++)p[e]=stringToUTF16String(c[e]);const o=Object.assign(Object.create(null),this.data.defaultAppearanceData);this.data.defaultAppearanceData.fontSize=0;this.data.defaultAppearanceData.fontName=g;[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h);this.data.defaultAppearanceData=o}else{this._isOffscreenCanvasSupported||warn("_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly.");[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h)}let y=f.descent;y=isNaN(y)?n*d:Math.max(n*d,Math.abs(y)*u);const w=Math.min(Math.floor((l-u)/2),1),D=this.data.textAlignment;if(this.data.multiLine)return this._getMultilineAppearance(E,p,f,u,Q,l,D,2,w,y,d,a);if(this.data.comb)return this._getCombAppearance(E,f,p[0],u,Q,l,2,w,y,d,a);const b=w+y;if(0===D||D>2)return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 ${numberToString(2)} ${numberToString(b)} Tm (${escapeString(p[0])}) Tj ET Q EMC`;return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 0 0 Tm ${this._renderText(p[0],f,u,Q,D,{shift:0},2,b)} ET Q EMC`}static async _getFontData(e,t,i,a){const s=new OperatorList,r={font:null,clone(){return this}},{fontName:n,fontSize:g}=i;await e.handleSetFont(a,[n&&Name.get(n),g],null,s,t,r,null);return r.font}_getTextWidth(e,t){return t.charsToGlyphs(e).reduce(((e,t)=>e+t.width),0)/1e3}_computeFontSize(e,t,i,a,r){let{fontSize:n}=this.data.defaultAppearanceData,g=(n||12)*s,o=Math.round(e/g);if(!n){const roundWithTwoDigits=e=>Math.floor(100*e)/100;if(-1===r){const r=this._getTextWidth(i,a);n=roundWithTwoDigits(Math.min(e/s,t/r));o=1}else{const c=i.split(/\r\n?|\n/),C=[];for(const e of c){const t=a.encodeString(e).join(""),i=a.charsToGlyphs(t),s=a.getCharPositions(t);C.push({line:t,glyphs:i,positions:s})}const isTooBig=i=>{let s=0;for(const r of C){s+=this._splitLine(null,a,i,t,r).length*i;if(s>e)return!0}return!1};o=Math.max(o,r);for(;;){g=e/o;n=roundWithTwoDigits(g/s);if(!isTooBig(n))break;o++}}const{fontName:c,fontColor:C}=this.data.defaultAppearanceData;this._defaultAppearance=function createDefaultAppearance({fontSize:e,fontName:t,fontColor:i}){return`/${escapePDFName(t)} ${e} Tf ${getPdfColor(i,!0)}`}({fontSize:n,fontName:c,fontColor:C})}return[this._defaultAppearance,n,e/o]}_renderText(e,t,i,a,s,r,n,g){let o;if(1===s){o=(a-this._getTextWidth(e,t)*i)/2}else if(2===s){o=a-this._getTextWidth(e,t)*i-n}else o=n;const c=numberToString(o-r.shift);r.shift=o;return`${c} ${g=numberToString(g)} Td (${escapeString(e)}) Tj`}_getSaveFieldResources(e){const{localResources:t,appearanceResources:i,acroFormResources:a}=this._fieldResources,s=this.data.defaultAppearanceData?.fontName;if(!s)return t||Dict.empty;for(const e of[t,i])if(e instanceof Dict){const t=e.get("Font");if(t instanceof Dict&&t.has(s))return e}if(a instanceof Dict){const i=a.get("Font");if(i instanceof Dict&&i.has(s)){const a=new Dict(e);a.set(s,i.getRaw(s));const r=new Dict(e);r.set("Font",a);return Dict.merge({xref:e,dictArray:[r,t],mergeSubDicts:!0})}}return t||Dict.empty}getFieldObject(){return null}}class TextWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t}=e;if(t.has("PMD")){this.flags|=z;this.data.hidden=!0;warn("Barcodes are not supported")}this.data.hasOwnCanvas=this.data.readOnly&&!this.data.noHTML;this._hasText=!0;"string"!=typeof this.data.fieldValue&&(this.data.fieldValue="");let i=getInheritableProperty({dict:t,key:"Q"});(!Number.isInteger(i)||i<0||i>2)&&(i=null);this.data.textAlignment=i;let a=getInheritableProperty({dict:t,key:"MaxLen"});(!Number.isInteger(a)||a<0)&&(a=0);this.data.maxLen=a;this.data.multiLine=this.hasFieldFlag(sA);this.data.comb=this.hasFieldFlag(hA)&&!this.hasFieldFlag(sA)&&!this.hasFieldFlag(rA)&&!this.hasFieldFlag(IA)&&0!==this.data.maxLen;this.data.doNotScroll=this.hasFieldFlag(CA)}get hasTextContent(){return!!this.appearance&&!this._needAppearances}_getCombAppearance(e,t,i,a,s,r,n,g,o,c,C){const h=s/this.data.maxLen,l=this.getBorderAndBackgroundAppearances(C),Q=[],E=t.getCharPositions(i);for(const[e,t]of E)Q.push(`(${escapeString(i.substring(e,t))}) Tj`);const u=Q.join(` ${numberToString(h)} 0 Td `);return`/Tx BMC q ${l}BT `+e+` 1 0 0 1 ${numberToString(n)} ${numberToString(g+o)} Tm ${u} ET Q EMC`}_getMultilineAppearance(e,t,i,a,s,r,n,g,o,c,C,h){const l=[],Q=s-2*g,E={shift:0};for(let e=0,r=t.length;e<r;e++){const r=t[e],h=this._splitLine(r,i,a,Q);for(let t=0,r=h.length;t<r;t++){const r=h[t],Q=0===e&&0===t?-o-(C-c):-C;l.push(this._renderText(r,i,a,s,n,E,g,Q))}}const u=this.getBorderAndBackgroundAppearances(h),d=l.join("\n");return`/Tx BMC q ${u}BT `+e+` 1 0 0 1 0 ${numberToString(r)} Tm ${d} ET Q EMC`}_splitLine(e,t,i,a,s={}){e=s.line||e;const r=s.glyphs||t.charsToGlyphs(e);if(r.length<=1)return[e];const n=s.positions||t.getCharPositions(e),g=i/1e3,o=[];let c=-1,C=-1,h=-1,l=0,Q=0;for(let t=0,i=r.length;t<i;t++){const[i,s]=n[t],E=r[t],u=E.width*g;if(" "===E.unicode)if(Q+u>a){o.push(e.substring(l,i));l=i;Q=u;c=-1;h=-1}else{Q+=u;c=i;C=s;h=t}else if(Q+u>a)if(-1!==c){o.push(e.substring(l,C));l=C;t=h+1;c=-1;Q=0}else{o.push(e.substring(l,i));l=i;Q=u}else Q+=u}l<e.length&&o.push(e.substring(l,e.length));return o}async extractTextContent(e,t,i){await super.extractTextContent(e,t,i);const a=this.data.textContent;if(!a)return;const s=a.join("\n");if(s===this.data.fieldValue)return;const r=s.replaceAll(/([.*+?^${}()|[\]\\])|(\s+)/g,((e,t)=>t?`\\${t}`:"\\s+"));new RegExp(`^\\s*${r}\\s*$`).test(this.data.fieldValue)&&(this.data.textContent=this.data.fieldValue.split("\n"))}getFieldObject(){return{id:this.data.id,value:this.data.fieldValue,defaultValue:this.data.defaultFieldValue||"",multiline:this.data.multiLine,password:this.hasFieldFlag(rA),charLimit:this.data.maxLen,comb:this.data.comb,editable:!this.data.readOnly,hidden:this.data.hidden,name:this.data.fieldName,rect:this.data.rect,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:"text"}}}class ButtonWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.checkedAppearance=null;this.uncheckedAppearance=null;this.data.checkBox=!this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.radioButton=this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.pushButton=this.hasFieldFlag(gA);this.data.isTooltipOnly=!1;if(this.data.checkBox)this._processCheckBox(e);else if(this.data.radioButton)this._processRadioButton(e);else if(this.data.pushButton){this.data.hasOwnCanvas=!0;this.data.noHTML=!1;this._processPushButton(e)}else warn("Invalid field flags for button widget annotation")}async getOperatorList(e,t,a,s){if(this.data.pushButton)return super.getOperatorList(e,t,a,!1,s);let r=null,n=null;if(s){const e=s.get(this.data.id);r=e?e.value:null;n=e?e.rotation:null}if(null===r&&this.appearance)return super.getOperatorList(e,t,a,s);null==r&&(r=this.data.checkBox?this.data.fieldValue===this.data.exportValue:this.data.fieldValue===this.data.buttonValue);const g=r?this.checkedAppearance:this.uncheckedAppearance;if(g){const r=this.appearance,o=lookupMatrix(g.dict.getArray("Matrix"),i);n&&g.dict.set("Matrix",this.getRotationMatrix(s));this.appearance=g;const c=super.getOperatorList(e,t,a,s);this.appearance=r;g.dict.set("Matrix",o);return c}return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}async save(e,t,i){return this.data.checkBox?this._saveCheckbox(e,t,i):this.data.radioButton?this._saveRadioButton(e,t,i):null}async _saveCheckbox(e,t,i){if(!i)return null;const a=i.get(this.data.id),s=this._buildFlags(a?.noView,a?.noPrint);let r=a?.rotation,n=a?.value;if(void 0===r&&void 0===s){if(void 0===n)return null;if(this.data.fieldValue===this.data.exportValue===n)return null}let g=e.xref.fetchIfRef(this.ref);if(!(g instanceof Dict))return null;g=g.clone();void 0===r&&(r=this.rotation);void 0===n&&(n=this.data.fieldValue===this.data.exportValue);const o={path:this.data.fieldName,value:n?this.data.exportValue:""},c=Name.get(n?this.data.exportValue:"Off");g.set("V",c);g.set("AS",c);g.set("M",`D:${getModificationDate()}`);void 0!==s&&g.set("F",s);const C=this._getMKDict(r);C&&g.set("MK",C);const h=[];await writeObject(this.ref,g,h,e.xref);return[{ref:this.ref,data:h.join(""),xfa:o}]}async _saveRadioButton(e,t,i){if(!i)return null;const a=i.get(this.data.id),s=this._buildFlags(a?.noView,a?.noPrint);let r=a?.rotation,n=a?.value;if(void 0===r&&void 0===s){if(void 0===n)return null;if(this.data.fieldValue===this.data.buttonValue===n)return null}let g=e.xref.fetchIfRef(this.ref);if(!(g instanceof Dict))return null;g=g.clone();void 0===n&&(n=this.data.fieldValue===this.data.buttonValue);void 0===r&&(r=this.rotation);const o={path:this.data.fieldName,value:n?this.data.buttonValue:""},c=Name.get(n?this.data.buttonValue:"Off"),C=[];let h=null;if(n)if(this.parent instanceof Ref){const t=e.xref.fetch(this.parent);t.set("V",c);await writeObject(this.parent,t,C,e.xref);h=C.join("");C.length=0}else this.parent instanceof Dict&&this.parent.set("V",c);this.parent||g.set("V",c);g.set("AS",c);g.set("M",`D:${getModificationDate()}`);void 0!==s&&g.set("F",s);const l=this._getMKDict(r);l&&g.set("MK",l);await writeObject(this.ref,g,C,e.xref);const Q=[{ref:this.ref,data:C.join(""),xfa:o}];h&&Q.push({ref:this.parent,data:h,xfa:null});return Q}_getDefaultCheckedAppearance(e,t){const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=[0,0,i,a],r=.8*Math.min(i,a);let n,g;if("check"===t){n={width:.755*r,height:.705*r};g="3"}else if("disc"===t){n={width:.791*r,height:.705*r};g="l"}else unreachable(`_getDefaultCheckedAppearance - unsupported type: ${t}`);const o=`q BT /PdfJsZaDb ${r} Tf 0 g ${numberToString((i-n.width)/2)} ${numberToString((a-n.height)/2)} Td (${g}) Tj ET Q`,c=new Dict(e.xref);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Matrix",[1,0,0,1,0,0]);c.set("Length",o.length);const C=new Dict(e.xref),h=new Dict(e.xref);h.set("PdfJsZaDb",this.fallbackFontDict);C.set("Font",h);c.set("Resources",C);this.checkedAppearance=new StringStream(o);this.checkedAppearance.dict=c;this._streams.push(this.checkedAppearance)}_processCheckBox(e){const t=e.dict.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(!(i instanceof Dict))return;const a=this._decodeFormValue(e.dict.get("AS"));"string"==typeof a&&(this.data.fieldValue=a);const s=null!==this.data.fieldValue&&"Off"!==this.data.fieldValue?this.data.fieldValue:"Yes",r=i.getKeys();if(0===r.length)r.push("Off",s);else if(1===r.length)"Off"===r[0]?r.push(s):r.unshift("Off");else if(r.includes(s)){r.length=0;r.push("Off",s)}else{const e=r.find((e=>"Off"!==e));r.length=0;r.push("Off",e)}r.includes(this.data.fieldValue)||(this.data.fieldValue="Off");this.data.exportValue=r[1];const n=i.get(this.data.exportValue);this.checkedAppearance=n instanceof BaseStream?n:null;const g=i.get("Off");this.uncheckedAppearance=g instanceof BaseStream?g:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"check");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processRadioButton(e){this.data.buttonValue=null;const t=e.dict.get("Parent");if(t instanceof Dict){this.parent=e.dict.getRaw("Parent");const i=t.get("V");i instanceof Name&&(this.data.fieldValue=this._decodeFormValue(i))}const i=e.dict.get("AP");if(!(i instanceof Dict))return;const a=i.get("N");if(!(a instanceof Dict))return;for(const e of a.getKeys())if("Off"!==e){this.data.buttonValue=this._decodeFormValue(e);break}const s=a.get(this.data.buttonValue);this.checkedAppearance=s instanceof BaseStream?s:null;const r=a.get("Off");this.uncheckedAppearance=r instanceof BaseStream?r:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"disc");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processPushButton(e){const{dict:t,annotationGlobals:i}=e;if(t.has("A")||t.has("AA")||this.data.alternativeText){this.data.isTooltipOnly=!t.has("A")&&!t.has("AA");Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}else warn("Push buttons without action dictionaries are not supported")}getFieldObject(){let e,t="button";if(this.data.checkBox){t="checkbox";e=this.data.exportValue}else if(this.data.radioButton){t="radiobutton";e=this.data.buttonValue}return{id:this.data.id,value:this.data.fieldValue||"Off",defaultValue:this.data.defaultFieldValue,exportValues:e,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,hidden:this.data.hidden,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:t}}get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("ZapfDingbats"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("ZapfDingbatsEncoding"));return shadow(this,"fallbackFontDict",e)}}class ChoiceWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.indices=t.getArray("I");this.hasIndices=Array.isArray(this.indices)&&this.indices.length>0;this.data.options=[];const a=getInheritableProperty({dict:t,key:"Opt"});if(Array.isArray(a))for(let e=0,t=a.length;e<t;e++){const t=i.fetchIfRef(a[e]),s=Array.isArray(t);this.data.options[e]={exportValue:this._decodeFormValue(s?i.fetchIfRef(t[0]):t),displayValue:this._decodeFormValue(s?i.fetchIfRef(t[1]):t)}}if(this.hasIndices){this.data.fieldValue=[];const e=this.data.options.length;for(const t of this.indices)Number.isInteger(t)&&t>=0&&t<e&&this.data.fieldValue.push(this.data.options[t].exportValue)}else"string"==typeof this.data.fieldValue?this.data.fieldValue=[this.data.fieldValue]:this.data.fieldValue||(this.data.fieldValue=[]);this.data.combo=this.hasFieldFlag(oA);this.data.multiSelect=this.hasFieldFlag(cA);this._hasText=!0}getFieldObject(){const e=this.data.combo?"combobox":"listbox",t=this.data.fieldValue.length>0?this.data.fieldValue[0]:null;return{id:this.data.id,value:t,defaultValue:this.data.defaultFieldValue,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,numItems:this.data.fieldValue.length,multipleSelection:this.data.multiSelect,hidden:this.data.hidden,actions:this.data.actions,items:this.data.options,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:e}}amendSavedDict(e,t){if(!this.hasIndices)return;let i=e?.get(this.data.id)?.value;Array.isArray(i)||(i=[i]);const a=[],{options:s}=this.data;for(let e=0,t=0,r=s.length;e<r;e++)if(s[e].exportValue===i[t]){a.push(e);t+=1}t.set("I",a)}async _getAppearance(e,t,i,a){if(this.data.combo)return super._getAppearance(e,t,i,a);let r,n;const g=a?.get(this.data.id);if(g){n=g.rotation;r=g.value}if(void 0===n&&void 0===r&&!this._needAppearances)return null;void 0===r?r=this.data.fieldValue:Array.isArray(r)||(r=[r]);let o=this.data.rect[3]-this.data.rect[1],c=this.data.rect[2]-this.data.rect[0];90!==n&&270!==n||([c,o]=[o,c]);const C=this.data.options.length,h=[];for(let e=0;e<C;e++){const{exportValue:t}=this.data.options[e];r.includes(t)&&h.push(e)}this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));const l=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);let Q,{fontSize:E}=this.data.defaultAppearanceData;if(E)Q=this._defaultAppearance;else{const e=(o-1)/C;let t,i=-1;for(const{displayValue:e}of this.data.options){const a=this._getTextWidth(e,l);if(a>i){i=a;t=e}}[Q,E]=this._computeFontSize(e,c-4,t,l,-1)}const u=E*s,d=(u-E)/2,f=Math.floor(o/u);let p=0;if(h.length>0){const e=Math.min(...h),t=Math.max(...h);p=Math.max(0,t-f+1);p>e&&(p=e)}const m=Math.min(p+f+1,C),y=["/Tx BMC q",`1 1 ${c} ${o} re W n`];if(h.length){y.push("0.600006 0.756866 0.854904 rg");for(const e of h)p<=e&&e<m&&y.push(`1 ${o-(e-p+1)*u} ${c} ${u} re f`)}y.push("BT",Q,`1 0 0 1 0 ${o} Tm`);const w={shift:0};for(let e=p;e<m;e++){const{displayValue:t}=this.data.options[e],i=e===p?d:0;y.push(this._renderText(t,l,E,c,0,w,2,-u+i))}y.push("ET Q EMC");return y.join("\n")}}class SignatureWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.data.fieldValue=null;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!this.data.hasOwnCanvas}getFieldObject(){return{id:this.data.id,value:null,page:this.data.pageIndex,type:"signature"}}}class TextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.noRotate=!0;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t}=e;this.data.annotationType=k;if(this.data.hasAppearance)this.data.name="NoIcon";else{this.data.rect[1]=this.data.rect[3]-22;this.data.rect[2]=this.data.rect[0]+22;this.data.name=t.has("Name")?t.get("Name").name:"Note"}if(t.has("State")){this.data.state=t.get("State")||null;this.data.stateModel=t.get("StateModel")||null}else{this.data.state=null;this.data.stateModel=null}}}class LinkAnnotation extends Annotation{constructor(e){super(e);const{dict:t,annotationGlobals:i}=e;this.data.annotationType=R;this.data.noHTML=!1;const a=getQuadPoints(t,this.rectangle);a&&(this.data.quadPoints=a);this.data.borderColor||=this.data.color;Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}}class PopupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;this.data.annotationType=O;this.data.noHTML=!1;this.data.rect[0]!==this.data.rect[2]&&this.data.rect[1]!==this.data.rect[3]||(this.data.rect=null);let i=t.get("Parent");if(!i){warn("Popup annotation has a missing or invalid parent annotation.");return}this.data.parentRect=lookupNormalRect(i.getArray("Rect"),null);isName(i.get("RT"),X)&&(i=i.get("IRT"));if(i.has("M")){this.setModificationDate(i.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;if(i.has("C")){this.setColor(i.getArray("C"));this.data.color=this.color}else this.data.color=null;if(!this.viewable){const e=i.get("F");this._isViewable(e)&&this.setFlags(e)}this.setTitle(i.get("T"));this.data.titleObj=this._title;this.setContents(i.get("Contents"));this.data.contentsObj=this._contents;i.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(i.get("RC")));this.data.open=!!t.get("Open")}}class FreeTextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;const{evaluatorOptions:t,xref:i}=e;this.data.annotationType=N;this.setDefaultAppearance(e);this._hasAppearance=!!this.appearance;if(this._hasAppearance){const{fontColor:e,fontSize:a}=function parseAppearanceStream(e,t,i){return new AppearanceStreamEvaluator(e,t,i).parse()}(this.appearance,t,i);this.data.defaultAppearanceData.fontColor=e;this.data.defaultAppearanceData.fontSize=a||10}else{this.data.defaultAppearanceData.fontSize||=10;const{fontColor:t,fontSize:a}=this.data.defaultAppearanceData;if(this._contents.str){this.data.textContent=this._contents.str.split(/\r\n?|\n/).map((e=>e.trimEnd()));const{coords:e,bbox:t,matrix:i}=FakeUnicodeFont.getFirstPositionInfo(this.rectangle,this.rotation,a);this.data.textPosition=this._transformPoint(e,t,i)}if(this._isOffscreenCanvasSupported){const s=e.dict.get("CA"),r=new FakeUnicodeFont(i,"sans-serif");this.appearance=r.createAppearance(this._contents.str,this.rectangle,this.rotation,a,t,s);this._streams.push(this.appearance)}else warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.")}}get hasTextContent(){return this._hasAppearance}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,fontSize:r,oldAnnotation:n,rect:g,rotation:o,user:c,value:C}=e,h=n||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("FreeText"));if(n){h.set("M",`D:${getModificationDate()}`);h.delete("RC")}else h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);const l=`/Helv ${r} Tf ${getPdfColor(s,!0)}`;h.set("DA",l);h.set("Contents",stringToAsciiOrUTF16BE(C));h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);i?e.set("N",i):e.set("N",a)}return h}static async createNewAppearanceStream(e,t,i){const{baseFontRef:a,evaluator:r,task:n}=i,{color:g,fontSize:o,rect:c,rotation:C,value:h}=e,l=new Dict(t),Q=new Dict(t);if(a)Q.set("Helv",a);else{const e=new Dict(t);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));Q.set("Helv",e)}l.set("Font",Q);const E=await WidgetAnnotation._getFontData(r,n,{fontName:"Helv",fontSize:o},l),[u,d,f,p]=c;let m=f-u,y=p-d;C%180!=0&&([m,y]=[y,m]);const w=h.split("\n"),D=o/1e3;let b=-1/0;const F=[];for(let e of w){const t=E.encodeString(e);if(t.length>1)return null;e=t.join("");F.push(e);let i=0;const a=E.charsToGlyphs(e);for(const e of a)i+=e.width*D;b=Math.max(b,i)}let S=1;b>m&&(S=m/b);let k=1;const R=s*o,N=1*o,G=R*w.length;G>y&&(k=y/G);const x=o*Math.min(S,k);let U,M,L;switch(C){case 0:L=[1,0,0,1];M=[c[0],c[1],m,y];U=[c[0],c[3]-N];break;case 90:L=[0,1,-1,0];M=[c[1],-c[2],m,y];U=[c[1],-c[0]-N];break;case 180:L=[-1,0,0,-1];M=[-c[2],-c[3],m,y];U=[-c[2],-c[1]-N];break;case 270:L=[0,-1,1,0];M=[-c[3],c[0],m,y];U=[-c[3],c[2]-N]}const H=["q",`${L.join(" ")} 0 0 cm`,`${M.join(" ")} re W n`,"BT",`${getPdfColor(g,!0)}`,`0 Tc /Helv ${numberToString(x)} Tf`];H.push(`${U.join(" ")} Td (${escapeString(F[0])}) Tj`);const J=numberToString(R);for(let e=1,t=F.length;e<t;e++){const t=F[e];H.push(`0 -${J} Td (${escapeString(t)}) Tj`)}H.push("ET","Q");const Y=H.join("\n"),v=new Dict(t);v.set("FormType",1);v.set("Subtype",Name.get("Form"));v.set("Type",Name.get("XObject"));v.set("BBox",c);v.set("Resources",l);v.set("Matrix",[1,0,0,1,-c[0],-c[1]]);const K=new StringStream(Y);K.dict=v;return K}}class LineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=G;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const a=lookupRect(t.getArray("L"),[0,0,0,0]);this.data.lineCoordinates=Util.normalizeRect(a);this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],s=t.get("CA"),r=getRgbColor(t.getArray("IC"),null),n=r?getPdfColorArray(r):null,g=n?s:null,o=this.borderStyle.width||1,c=2*o,C=[this.data.lineCoordinates[0]-c,this.data.lineCoordinates[1]-c,this.data.lineCoordinates[2]+c,this.data.lineCoordinates[3]+c];Util.intersect(this.rectangle,C)||(this.rectangle=C);this._setDefaultAppearance({xref:i,extra:`${o} w`,strokeColor:e,fillColor:n,strokeAlpha:s,fillAlpha:g,pointsCallback:(e,t)=>{e.push(`${a[0]} ${a[1]} m`,`${a[2]} ${a[3]} l`,"S");return[t[0]-o,t[2]+o,t[7]-o,t[3]+o]}})}}}class SquareAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=x;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[4]+this.borderStyle.width/2,a=t[5]+this.borderStyle.width/2,s=t[6]-t[4]-this.borderStyle.width,n=t[3]-t[7]-this.borderStyle.width;e.push(`${i} ${a} ${s} ${n} re`);r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class CircleAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=U;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;const g=4/3*Math.tan(Math.PI/8);this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[0]+this.borderStyle.width/2,a=t[1]-this.borderStyle.width/2,s=t[6]-this.borderStyle.width/2,n=t[7]+this.borderStyle.width/2,o=i+(s-i)/2,c=a+(n-a)/2,C=(s-i)/2*g,h=(n-a)/2*g;e.push(`${o} ${n} m`,`${o+C} ${n} ${s} ${c+h} ${s} ${c} c`,`${s} ${c-h} ${o+C} ${a} ${o} ${a} c`,`${o-C} ${a} ${i} ${c-h} ${i} ${c} c`,`${i} ${c+h} ${o-C} ${n} ${o} ${n} c`,"h");r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolylineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=L;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.vertices=null;if(!(this instanceof PolygonAnnotation)){this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings}const a=t.getArray("Vertices");if(!isNumberArray(a,null))return;const s=this.data.vertices=Float32Array.from(a);if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),r=this.borderStyle.width||1,n=2*r,g=[1/0,1/0,-1/0,-1/0];for(let e=0,t=s.length;e<t;e+=2){g[0]=Math.min(g[0],s[e]-n);g[1]=Math.min(g[1],s[e+1]-n);g[2]=Math.max(g[2],s[e]+n);g[3]=Math.max(g[3],s[e+1]+n)}Util.intersect(this.rectangle,g)||(this.rectangle=g);this._setDefaultAppearance({xref:i,extra:`${r} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(let t=0,i=s.length;t<i;t+=2)e.push(`${s[t]} ${s[t+1]} ${0===t?"m":"l"}`);e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolygonAnnotation extends PolylineAnnotation{constructor(e){super(e);this.data.annotationType=M}}class CaretAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.annotationType=T}}class InkAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t,xref:i}=e;this.data.annotationType=q;this.data.inkLists=[];this.data.isEditable=!this.data.noHTML&&"InkHighlight"===this.data.it;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;const a=t.getArray("InkList");if(Array.isArray(a)){for(let e=0,t=a.length;e<t;++e){if(!Array.isArray(a[e]))continue;const t=new Float32Array(a[e].length);this.data.inkLists.push(t);for(let s=0,r=a[e].length;s<r;s+=2){const r=i.fetchIfRef(a[e][s]),n=i.fetchIfRef(a[e][s+1]);if("number"==typeof r&&"number"==typeof n){t[s]=r;t[s+1]=n}}}if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=this.borderStyle.width||1,r=2*s,n=[1/0,1/0,-1/0,-1/0];for(const e of this.data.inkLists)for(let t=0,i=e.length;t<i;t+=2){n[0]=Math.min(n[0],e[t]-r);n[1]=Math.min(n[1],e[t+1]-r);n[2]=Math.max(n[2],e[t]+r);n[3]=Math.max(n[3],e[t+1]+r)}Util.intersect(this.rectangle,n)||(this.rectangle=n);this._setDefaultAppearance({xref:i,extra:`${s} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(const t of this.data.inkLists){for(let i=0,a=t.length;i<a;i+=2)e.push(`${t[i]} ${t[i+1]} ${0===i?"m":"l"}`);e.push("S")}return[t[0],t[2],t[7],t[3]]}})}}}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,opacity:r,paths:n,outlines:g,rect:o,rotation:c,thickness:C}=e,h=new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("Ink"));h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",o);h.set("InkList",g?.points||n.map((e=>e.points)));h.set("F",4);h.set("Rotate",c);g&&h.set("IT",Name.get("InkHighlight"));const l=new Dict(t);h.set("BS",l);l.set("W",C);h.set("C",Array.from(s,(e=>e/255)));h.set("CA",r);const Q=new Dict(t);h.set("AP",Q);i?Q.set("N",i):Q.set("N",a);return h}static async createNewAppearanceStream(e,t,i){if(e.outlines)return this.createNewAppearanceStreamForHighlight(e,t,i);const{color:a,rect:s,paths:r,thickness:n,opacity:g}=e,o=[`${n} w 1 J 1 j`,`${getPdfColor(a,!1)}`];1!==g&&o.push("/R0 gs");const c=[];for(const{bezier:e}of r){c.length=0;c.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);if(2===e.length)c.push(`${numberToString(e[0])} ${numberToString(e[1])} l S`);else{for(let t=2,i=e.length;t<i;t+=6){const i=e.slice(t,t+6).map(numberToString).join(" ");c.push(`${i} c`)}c.push("S")}o.push(c.join("\n"))}const C=o.join("\n"),h=new Dict(t);h.set("FormType",1);h.set("Subtype",Name.get("Form"));h.set("Type",Name.get("XObject"));h.set("BBox",s);h.set("Length",C.length);if(1!==g){const e=new Dict(t),i=new Dict(t),a=new Dict(t);a.set("CA",g);a.set("Type",Name.get("ExtGState"));i.set("R0",a);e.set("ExtGState",i);h.set("Resources",e)}const l=new StringStream(C);l.dict=h;return l}static async createNewAppearanceStreamForHighlight(e,t,i){const{color:a,rect:s,outlines:{outline:r},opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"];g.push(`${numberToString(r[4])} ${numberToString(r[5])} m`);for(let e=6,t=r.length;e<t;e+=6)if(isNaN(r[e])||null===r[e])g.push(`${numberToString(r[e+4])} ${numberToString(r[e+5])} l`);else{const t=r.slice(e,e+6).map(numberToString).join(" ");g.push(`${t} c`)}g.push("h f");const o=g.join("\n"),c=new Dict(t);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Length",o.length);const C=new Dict(t),h=new Dict(t);C.set("ExtGState",h);c.set("Resources",C);const l=new Dict(t);h.set("R0",l);l.set("BM",Name.get("Multiply"));if(1!==n){l.set("ca",n);l.set("Type",Name.get("ExtGState"))}const Q=new StringStream(o);Q.dict=c;return Q}}class HighlightAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=H;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;if(this.data.quadPoints=getQuadPoints(t,null)){const e=this.appearance?.dict.get("Resources");if(!this.appearance||!e?.has("ExtGState")){this.appearance&&warn("HighlightAnnotation - ignoring built-in appearance stream.");const e=this.color?getPdfColorArray(this.color):[1,1,0],a=t.get("CA");this._setDefaultAppearance({xref:i,fillColor:e,blendMode:"Multiply",fillAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[0]} ${t[1]} m`,`${t[2]} ${t[3]} l`,`${t[6]} ${t[7]} l`,`${t[4]} ${t[5]} l`,"f");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,oldAnnotation:r,opacity:n,rect:g,rotation:o,user:c,quadPoints:C}=e,h=r||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("Highlight"));h.set(r?"M":"CreationDate",`D:${getModificationDate()}`);h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);h.set("QuadPoints",C);h.set("C",Array.from(s,(e=>e/255)));h.set("CA",n);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);e.set("N",i||a)}return h}static async createNewAppearanceStream(e,t,i){const{color:a,rect:s,outlines:r,opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"],o=[];for(const e of r){o.length=0;o.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);for(let t=2,i=e.length;t<i;t+=2)o.push(`${numberToString(e[t])} ${numberToString(e[t+1])} l`);o.push("h");g.push(o.join("\n"))}g.push("f*");const c=g.join("\n"),C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",s);C.set("Length",c.length);const h=new Dict(t),l=new Dict(t);h.set("ExtGState",l);C.set("Resources",h);const Q=new Dict(t);l.set("R0",Q);Q.set("BM",Name.get("Multiply"));if(1!==n){Q.set("ca",n);Q.set("Type",Name.get("ExtGState"))}const E=new StringStream(c);E.dict=C;return E}}class UnderlineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=J;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 0.571 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[4]} ${t[5]+1.3} m`,`${t[6]} ${t[7]+1.3} l`,"S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class SquigglyAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=Y;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{const i=(t[1]-t[5])/6;let a=i,s=t[4];const r=t[5],n=t[6];e.push(`${s} ${r+a} m`);do{s+=2;a=0===a?i:0;e.push(`${s} ${r+a} l`)}while(s<n);e.push("S");return[t[4],n,r-2*i,r+2*i]}})}}else this.data.popupRef=null}}class StrikeOutAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=v;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push((t[0]+t[4])/2+" "+(t[1]+t[5])/2+" m",(t[2]+t[6])/2+" "+(t[3]+t[7])/2+" l","S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class StampAnnotation extends MarkupAnnotation{#T;constructor(e){super(e);this.data.annotationType=K;this.#T=this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1}mustBeViewedWhenEditing(e,t=null){if(e){if(!this.data.isEditable)return!1;this.#T=this.data.hasOwnCanvas;this.data.hasOwnCanvas=!0;return!0}this.data.hasOwnCanvas=this.#T;return!t?.has(this.data.id)}static async createImage(e,t){const{width:i,height:a}=e,s=new OffscreenCanvas(i,a),r=s.getContext("2d",{alpha:!0});r.drawImage(e,0,0);const n=r.getImageData(0,0,i,a).data,g=new Uint32Array(n.buffer),o=g.some(FeatureTest.isLittleEndian?e=>e>>>24!=255:e=>255!=(255&e));if(o){r.fillStyle="white";r.fillRect(0,0,i,a);r.drawImage(e,0,0)}const c=s.convertToBlob({type:"image/jpeg",quality:1}).then((e=>e.arrayBuffer())),C=Name.get("XObject"),h=Name.get("Image"),l=new Dict(t);l.set("Type",C);l.set("Subtype",h);l.set("BitsPerComponent",8);l.set("ColorSpace",Name.get("DeviceRGB"));l.set("Filter",Name.get("DCTDecode"));l.set("BBox",[0,0,i,a]);l.set("Width",i);l.set("Height",a);let Q=null;if(o){const e=new Uint8Array(g.length);if(FeatureTest.isLittleEndian)for(let t=0,i=g.length;t<i;t++)e[t]=g[t]>>>24;else for(let t=0,i=g.length;t<i;t++)e[t]=255&g[t];const s=new Dict(t);s.set("Type",C);s.set("Subtype",h);s.set("BitsPerComponent",8);s.set("ColorSpace",Name.get("DeviceGray"));s.set("Width",i);s.set("Height",a);Q=new Stream(e,0,0,s)}return{imageStream:new Stream(await c,0,0,l),smaskStream:Q,width:i,height:a}}static createNewDict(e,t,{apRef:i,ap:a}){const{oldAnnotation:s,rect:r,rotation:n,user:g}=e,o=s||new Dict(t);o.set("Type",Name.get("Annot"));o.set("Subtype",Name.get("Stamp"));o.set(s?"M":"CreationDate",`D:${getModificationDate()}`);o.set("CreationDate",`D:${getModificationDate()}`);o.set("Rect",r);o.set("F",4);o.set("Border",[0,0,0]);o.set("Rotate",n);g&&o.set("T",stringToAsciiOrUTF16BE(g));if(i||a){const e=new Dict(t);o.set("AP",e);i?e.set("N",i):e.set("N",a)}return o}static async createNewAppearanceStream(e,t,i){if(e.oldAnnotation)return null;const{rotation:a}=e,{imageRef:s,width:r,height:n}=i.image,g=new Dict(t),o=new Dict(t);g.set("XObject",o);o.set("Im0",s);const c=`q ${r} 0 0 ${n} 0 0 cm /Im0 Do Q`,C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",[0,0,r,n]);C.set("Resources",g);if(a){const e=getRotationMatrix(a,r,n);C.set("Matrix",e)}const h=new StringStream(c);h.dict=C;return h}}class FileAttachmentAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e,a=new FileSpec(t.get("FS"),i);this.data.annotationType=W;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.file=a.serializable;const s=t.get("Name");this.data.name=s instanceof Name?stringToPDFString(s.name):"PushPin";const r=t.get("ca");this.data.fillAlpha="number"==typeof r&&r>=0&&r<=1?r:null}}function decodeString(e){try{return stringToUTF8String(e)}catch(t){warn(`UTF-8 decoding failed: "${t}".`);return e}}class DatasetXMLParser extends SimpleXMLParser{constructor(e){super(e);this.node=null}onEndElement(e){const t=super.onEndElement(e);if(t&&"xfa:datasets"===e){this.node=t;throw new Error("Aborting DatasetXMLParser.")}}}class DatasetReader{constructor(e){if(e.datasets)this.node=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e.datasets).documentElement;else{const t=new DatasetXMLParser({hasAttributes:!0});try{t.parseFromString(e["xdp:xdp"])}catch{}this.node=t.node}}getValue(e){if(!this.node||!e)return"";const t=this.node.searchNode(parseXFAPath(e),0);if(!t)return"";const i=t.firstChild;return"value"===i?.nodeName?t.children.map((e=>decodeString(e.textContent))):decodeString(t.textContent)}}class XRef{#q=null;constructor(e,t){this.stream=e;this.pdfManager=t;this.entries=[];this._xrefStms=new Set;this._cacheMap=new Map;this._pendingRefs=new RefSet;this._newPersistentRefNum=null;this._newTemporaryRefNum=null;this._persistentRefsCache=null}getNewPersistentRef(e){null===this._newPersistentRefNum&&(this._newPersistentRefNum=this.entries.length||1);const t=this._newPersistentRefNum++;this._cacheMap.set(t,e);return Ref.get(t,0)}getNewTemporaryRef(){if(null===this._newTemporaryRefNum){this._newTemporaryRefNum=this.entries.length||1;if(this._newPersistentRefNum){this._persistentRefsCache=new Map;for(let e=this._newTemporaryRefNum;e<this._newPersistentRefNum;e++){this._persistentRefsCache.set(e,this._cacheMap.get(e));this._cacheMap.delete(e)}}}return Ref.get(this._newTemporaryRefNum++,0)}resetNewTemporaryRef(){this._newTemporaryRefNum=null;if(this._persistentRefsCache)for(const[e,t]of this._persistentRefsCache)this._cacheMap.set(e,t);this._persistentRefsCache=null}setStartXRef(e){this.startXRefQueue=[e]}parse(e=!1){let t,i,a;if(e){warn("Indexing all PDF objects");t=this.indexObjects()}else t=this.readXRef();t.assignXref(this);this.trailer=t;try{i=t.get("Encrypt")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Encrypt" reference: "${e}".`)}if(i instanceof Dict){const e=t.get("ID"),a=e?.length?e[0]:"";i.suppressEncryption=!0;this.encrypt=new CipherTransformFactory(i,a,this.pdfManager.password)}try{a=t.get("Root")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Root" reference: "${e}".`)}if(a instanceof Dict)try{if(a.get("Pages")instanceof Dict){this.root=a;return}}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Pages" reference: "${e}".`)}if(!e)throw new XRefParseException;throw new InvalidPDFException("Invalid Root reference.")}processXRefTable(e){"tableState"in this||(this.tableState={entryNum:0,streamPos:e.lexer.stream.pos,parserBuf1:e.buf1,parserBuf2:e.buf2});if(!isCmd(this.readXRefTable(e),"trailer"))throw new FormatError("Invalid XRef table: could not find trailer dictionary");let t=e.getObj();t instanceof Dict||!t.dict||(t=t.dict);if(!(t instanceof Dict))throw new FormatError("Invalid XRef table: could not parse trailer dictionary");delete this.tableState;return t}readXRefTable(e){const t=e.lexer.stream,i=this.tableState;t.pos=i.streamPos;e.buf1=i.parserBuf1;e.buf2=i.parserBuf2;let a;for(;;){if(!("firstEntryNum"in i)||!("entryCount"in i)){if(isCmd(a=e.getObj(),"trailer"))break;i.firstEntryNum=a;i.entryCount=e.getObj()}let s=i.firstEntryNum;const r=i.entryCount;if(!Number.isInteger(s)||!Number.isInteger(r))throw new FormatError("Invalid XRef table: wrong types in subsection header");for(let a=i.entryNum;a<r;a++){i.streamPos=t.pos;i.entryNum=a;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;const n={};n.offset=e.getObj();n.gen=e.getObj();const g=e.getObj();if(g instanceof Cmd)switch(g.cmd){case"f":n.free=!0;break;case"n":n.uncompressed=!0}if(!Number.isInteger(n.offset)||!Number.isInteger(n.gen)||!n.free&&!n.uncompressed)throw new FormatError(`Invalid entry in XRef subsection: ${s}, ${r}`);0===a&&n.free&&1===s&&(s=0);this.entries[a+s]||(this.entries[a+s]=n)}i.entryNum=0;i.streamPos=t.pos;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;delete i.firstEntryNum;delete i.entryCount}if(this.entries[0]&&!this.entries[0].free)throw new FormatError("Invalid XRef table: unexpected first object");return a}processXRefStream(e){if(!("streamState"in this)){const t=e.dict,i=t.get("W");let a=t.get("Index");a||(a=[0,t.get("Size")]);this.streamState={entryRanges:a,byteWidths:i,entryNum:0,streamPos:e.pos}}this.readXRefStream(e);delete this.streamState;return e.dict}readXRefStream(e){const t=this.streamState;e.pos=t.streamPos;const[i,a,s]=t.byteWidths,r=t.entryRanges;for(;r.length>0;){const[n,g]=r;if(!Number.isInteger(n)||!Number.isInteger(g))throw new FormatError(`Invalid XRef range fields: ${n}, ${g}`);if(!Number.isInteger(i)||!Number.isInteger(a)||!Number.isInteger(s))throw new FormatError(`Invalid XRef entry fields length: ${n}, ${g}`);for(let r=t.entryNum;r<g;++r){t.entryNum=r;t.streamPos=e.pos;let g=0,o=0,c=0;for(let t=0;t<i;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'type'.");g=g<<8|t}0===i&&(g=1);for(let t=0;t<a;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'offset'.");o=o<<8|t}for(let t=0;t<s;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'generation'.");c=c<<8|t}const C={};C.offset=o;C.gen=c;switch(g){case 0:C.free=!0;break;case 1:C.uncompressed=!0;break;case 2:break;default:throw new FormatError(`Invalid XRef entry type: ${g}`)}this.entries[n+r]||(this.entries[n+r]=C)}t.entryNum=0;t.streamPos=e.pos;r.splice(0,2)}}indexObjects(){function readToken(e,t){let i="",a=e[t];for(;10!==a&&13!==a&&60!==a&&!(++t>=e.length);){i+=String.fromCharCode(a);a=e[t]}return i}function skipUntil(e,t,i){const a=i.length,s=e.length;let r=0;for(;t<s;){let s=0;for(;s<a&&e[t+s]===i[s];)++s;if(s>=a)break;t++;r++}return r}const e=/\b(endobj|\d+\s+\d+\s+obj|xref|trailer\s*<<)\b/g,t=/\b(startxref|\d+\s+\d+\s+obj)\b/g,i=/^(\d+)\s+(\d+)\s+obj\b/,a=new Uint8Array([116,114,97,105,108,101,114]),s=new Uint8Array([115,116,97,114,116,120,114,101,102]),r=new Uint8Array([47,88,82,101,102]);this.entries.length=0;this._cacheMap.clear();const n=this.stream;n.pos=0;const g=n.getBytes(),o=bytesToString(g),c=g.length;let C=n.start;const h=[],l=[];for(;C<c;){let Q=g[C];if(9===Q||10===Q||13===Q||32===Q){++C;continue}if(37===Q){do{++C;if(C>=c)break;Q=g[C]}while(10!==Q&&13!==Q);continue}const E=readToken(g,C);let u;if(E.startsWith("xref")&&(4===E.length||/\s/.test(E[4]))){C+=skipUntil(g,C,a);h.push(C);C+=skipUntil(g,C,s)}else if(u=i.exec(E)){const t=0|u[1],i=0|u[2],a=C+E.length;let s,h=!1;if(this.entries[t]){if(this.entries[t].gen===i)try{new Parser({lexer:new Lexer(n.makeSubStream(a))}).getObj();h=!0}catch(e){e instanceof ParserEOFException?warn(`indexObjects -- checking object (${E}): "${e}".`):h=!0}}else h=!0;h&&(this.entries[t]={offset:C-n.start,gen:i,uncompressed:!0});e.lastIndex=a;const Q=e.exec(o);if(Q){s=e.lastIndex+1-C;if("endobj"!==Q[1]){warn(`indexObjects: Found "${Q[1]}" inside of another "obj", caused by missing "endobj" -- trying to recover.`);s-=Q[1].length+1}}else s=c-C;const d=g.subarray(C,C+s),f=skipUntil(d,0,r);if(f<s&&d[f+5]<64){l.push(C-n.start);this._xrefStms.add(C-n.start)}C+=s}else if(E.startsWith("trailer")&&(7===E.length||/\s/.test(E[7]))){h.push(C);const e=C+E.length;let i;t.lastIndex=e;const a=t.exec(o);if(a){i=t.lastIndex+1-C;if("startxref"!==a[1]){warn(`indexObjects: Found "${a[1]}" after "trailer", caused by missing "startxref" -- trying to recover.`);i-=a[1].length+1}}else i=c-C;C+=i}else C+=E.length+1}for(const e of l){this.startXRefQueue.push(e);this.readXRef(!0)}const Q=[];let E,u,d=!1;for(const e of h){n.pos=e;const t=new Parser({lexer:new Lexer(n),xref:this,allowStreams:!0,recoveryMode:!0});if(!isCmd(t.getObj(),"trailer"))continue;const i=t.getObj();if(i instanceof Dict){Q.push(i);i.has("Encrypt")&&(d=!0)}}for(const e of[...Q,"genFallback",...Q]){if("genFallback"===e){if(!u)break;this._generationFallback=!0;continue}let t=!1;try{const i=e.get("Root");if(!(i instanceof Dict))continue;const a=i.get("Pages");if(!(a instanceof Dict))continue;const s=a.get("Count");Number.isInteger(s)&&(t=!0)}catch(e){u=e;continue}if(t&&(!d||e.has("Encrypt"))&&e.has("ID"))return e;E=e}if(E)return E;if(this.topDict)return this.topDict;throw new InvalidPDFException("Invalid PDF structure.")}readXRef(e=!1){const t=this.stream,i=new Set;for(;this.startXRefQueue.length;){try{const e=this.startXRefQueue[0];if(i.has(e)){warn("readXRef - skipping XRef table since it was already parsed.");this.startXRefQueue.shift();continue}i.add(e);t.pos=e+t.start;const a=new Parser({lexer:new Lexer(t),xref:this,allowStreams:!0});let s,r=a.getObj();if(isCmd(r,"xref")){s=this.processXRefTable(a);this.topDict||(this.topDict=s);r=s.get("XRefStm");if(Number.isInteger(r)&&!this._xrefStms.has(r)){this._xrefStms.add(r);this.startXRefQueue.push(r);this.#q??=r}}else{if(!Number.isInteger(r))throw new FormatError("Invalid XRef stream header");if(!(Number.isInteger(a.getObj())&&isCmd(a.getObj(),"obj")&&(r=a.getObj())instanceof BaseStream))throw new FormatError("Invalid XRef stream");s=this.processXRefStream(r);this.topDict||(this.topDict=s);if(!s)throw new FormatError("Failed to read XRef stream")}r=s.get("Prev");Number.isInteger(r)?this.startXRefQueue.push(r):r instanceof Ref&&this.startXRefQueue.push(r.num)}catch(e){if(e instanceof MissingDataException)throw e;info("(while reading XRef): "+e)}this.startXRefQueue.shift()}if(this.topDict)return this.topDict;if(!e)throw new XRefParseException}get lastXRefStreamPos(){return this.#q??(this._xrefStms.size>0?Math.max(...this._xrefStms):null)}getEntry(e){const t=this.entries[e];return t&&!t.free&&t.offset?t:null}fetchIfRef(e,t=!1){return e instanceof Ref?this.fetch(e,t):e}fetch(e,t=!1){if(!(e instanceof Ref))throw new Error("ref object is not a reference");const i=e.num,a=this._cacheMap.get(i);if(void 0!==a){a instanceof Dict&&!a.objId&&(a.objId=e.toString());return a}let s=this.getEntry(i);if(null===s){this._cacheMap.set(i,s);return s}if(this._pendingRefs.has(e)){this._pendingRefs.remove(e);warn(`Ignoring circular reference: ${e}.`);return yt}this._pendingRefs.put(e);try{s=s.uncompressed?this.fetchUncompressed(e,s,t):this.fetchCompressed(e,s,t);this._pendingRefs.remove(e)}catch(t){this._pendingRefs.remove(e);throw t}s instanceof Dict?s.objId=e.toString():s instanceof BaseStream&&(s.dict.objId=e.toString());return s}fetchUncompressed(e,t,i=!1){const a=e.gen;let s=e.num;if(t.gen!==a){const r=`Inconsistent generation in XRef: ${e}`;if(this._generationFallback&&t.gen<a){warn(r);return this.fetchUncompressed(Ref.get(s,t.gen),t,i)}throw new XRefEntryException(r)}const r=this.stream.makeSubStream(t.offset+this.stream.start),n=new Parser({lexer:new Lexer(r),xref:this,allowStreams:!0}),g=n.getObj(),o=n.getObj(),c=n.getObj();if(g!==s||o!==a||!(c instanceof Cmd))throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`);if("obj"!==c.cmd){if(c.cmd.startsWith("obj")){s=parseInt(c.cmd.substring(3),10);if(!Number.isNaN(s))return s}throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`)}(t=this.encrypt&&!i?n.getObj(this.encrypt.createCipherTransform(s,a)):n.getObj())instanceof BaseStream||this._cacheMap.set(s,t);return t}fetchCompressed(e,t,i=!1){const a=t.offset,s=this.fetch(Ref.get(a,0));if(!(s instanceof BaseStream))throw new FormatError("bad ObjStm stream");const r=s.dict.get("First"),n=s.dict.get("N");if(!Number.isInteger(r)||!Number.isInteger(n))throw new FormatError("invalid first and n parameters for ObjStm stream");let g=new Parser({lexer:new Lexer(s),xref:this,allowStreams:!0});const o=new Array(n),c=new Array(n);for(let e=0;e<n;++e){const t=g.getObj();if(!Number.isInteger(t))throw new FormatError(`invalid object number in the ObjStm stream: ${t}`);const i=g.getObj();if(!Number.isInteger(i))throw new FormatError(`invalid object offset in the ObjStm stream: ${i}`);o[e]=t;c[e]=i}const C=(s.start||0)+r,h=new Array(n);for(let e=0;e<n;++e){const t=e<n-1?c[e+1]-c[e]:void 0;if(t<0)throw new FormatError("Invalid offset in the ObjStm stream.");g=new Parser({lexer:new Lexer(s.makeSubStream(C+c[e],t,s.dict)),xref:this,allowStreams:!0});const i=g.getObj();h[e]=i;if(i instanceof BaseStream)continue;const r=o[e],l=this.entries[r];l&&l.offset===a&&l.gen===e&&this._cacheMap.set(r,i)}if(void 0===(t=h[t.gen]))throw new XRefEntryException(`Bad (compressed) XRef entry: ${e}`);return t}async fetchIfRefAsync(e,t){return e instanceof Ref?this.fetchAsync(e,t):e}async fetchAsync(e,t){try{return this.fetch(e,t)}catch(i){if(!(i instanceof MissingDataException))throw i;await this.pdfManager.requestRange(i.begin,i.end);return this.fetchAsync(e,t)}}getCatalogObj(){return this.root}}const og=[0,0,612,792];class Page{constructor({pdfManager:e,xref:t,pageIndex:i,pageDict:a,ref:s,globalIdFactory:r,fontCache:n,builtInCMapCache:g,standardFontDataCache:o,globalImageCache:c,systemFontCache:C,nonBlendModesSet:h,xfaFactory:l}){this.pdfManager=e;this.pageIndex=i;this.pageDict=a;this.xref=t;this.ref=s;this.fontCache=n;this.builtInCMapCache=g;this.standardFontDataCache=o;this.globalImageCache=c;this.systemFontCache=C;this.nonBlendModesSet=h;this.evaluatorOptions=e.evaluatorOptions;this.resourcesPromise=null;this.xfaFactory=l;const Q={obj:0};this._localIdFactory=class extends r{static createObjId(){return`p${i}_${++Q.obj}`}static getPageObjId(){return`p${s.toString()}`}}}_getInheritableProperty(e,t=!1){const i=getInheritableProperty({dict:this.pageDict,key:e,getArray:t,stopWhenFound:!1});return Array.isArray(i)?1!==i.length&&i[0]instanceof Dict?Dict.merge({xref:this.xref,dictArray:i}):i[0]:i}get content(){return this.pageDict.getArray("Contents")}get resources(){const e=this._getInheritableProperty("Resources");return shadow(this,"resources",e instanceof Dict?e:Dict.empty)}_getBoundingBox(e){if(this.xfaData)return this.xfaData.bbox;const t=lookupNormalRect(this._getInheritableProperty(e,!0),null);if(t){if(t[2]-t[0]>0&&t[3]-t[1]>0)return t;warn(`Empty, or invalid, /${e} entry.`)}return null}get mediaBox(){return shadow(this,"mediaBox",this._getBoundingBox("MediaBox")||og)}get cropBox(){return shadow(this,"cropBox",this._getBoundingBox("CropBox")||this.mediaBox)}get userUnit(){let e=this.pageDict.get("UserUnit");("number"!=typeof e||e<=0)&&(e=1);return shadow(this,"userUnit",e)}get view(){const{cropBox:e,mediaBox:t}=this;if(e!==t&&!isArrayEqual(e,t)){const i=Util.intersect(e,t);if(i&&i[2]-i[0]>0&&i[3]-i[1]>0)return shadow(this,"view",i);warn("Empty /CropBox and /MediaBox intersection.")}return shadow(this,"view",t)}get rotate(){let e=this._getInheritableProperty("Rotate")||0;e%90!=0?e=0:e>=360?e%=360:e<0&&(e=(e%360+360)%360);return shadow(this,"rotate",e)}_onSubStreamError(e,t){if(!this.evaluatorOptions.ignoreErrors)throw e;warn(`getContentStream - ignoring sub-stream (${t}): "${e}".`)}getContentStream(){return this.pdfManager.ensure(this,"content").then((e=>e instanceof BaseStream?e:Array.isArray(e)?new StreamsSequenceStream(e,this._onSubStreamError.bind(this)):new NullStream))}get xfaData(){return shadow(this,"xfaData",this.xfaFactory?{bbox:this.xfaFactory.getBoundingBox(this.pageIndex)}:null)}async#O(e,t,i){const a=[];for(const s of e)if(s.id){const e=Ref.fromString(s.id);if(!e){warn(`A non-linked annotation cannot be modified: ${s.id}`);continue}if(s.deleted){t.put(e,e);if(s.popupRef){const e=Ref.fromString(s.popupRef);e&&t.put(e,e)}continue}i?.put(e);s.ref=e;a.push(this.xref.fetchAsync(e).then((e=>{e instanceof Dict&&(s.oldAnnotation=e.clone())}),(()=>{warn(`Cannot fetch \`oldAnnotation\` for: ${e}.`)})));delete s.id}await Promise.all(a)}async saveNewAnnotations(e,t,i,a){if(this.xfaFactory)throw new Error("XFA: Cannot save new annotations.");const s=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),r=new RefSetCache,n=new RefSet;await this.#O(i,r,n);const g=this.pageDict,o=this.annotations.filter((e=>!(e instanceof Ref&&r.has(e)))),c=await AnnotationFactory.saveNewAnnotations(s,t,i,a);for(const{ref:e}of c.annotations)e instanceof Ref&&!n.has(e)&&o.push(e);const C=g.get("Annots");g.set("Annots",o);const h=[];await writeObject(this.ref,g,h,this.xref);C&&g.set("Annots",C);const l=c.dependencies;l.push({ref:this.ref,data:h.join("")},...c.annotations);for(const e of r)l.push({ref:e,data:null});return l}save(e,t,i){const a=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});return this._parsedAnnotations.then((function(e){const s=[];for(const r of e)s.push(r.save(a,t,i).catch((function(e){warn(`save - ignoring annotation data during "${t.name}" task: "${e}".`);return null})));return Promise.all(s).then((function(e){return e.filter((e=>!!e))}))}))}loadResources(e){this.resourcesPromise||=this.pdfManager.ensure(this,"resources");return this.resourcesPromise.then((()=>new ObjectLoader(this.resources,e,this.xref).load()))}getOperatorList({handler:e,sink:t,task:i,intent:a,cacheKey:s,annotationStorage:r=null,modifiedIds:n=null}){const C=this.getContentStream(),E=this.loadResources(["ColorSpace","ExtGState","Font","Pattern","Properties","Shading","XObject"]),d=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),f=this.xfaFactory?null:getNewAnnotationsMap(r),p=f?.get(this.pageIndex);let m=Promise.resolve(null),y=null;if(p){const e=this.pdfManager.ensureDoc("annotationGlobals");let t;const a=new Set;for(const{bitmapId:e,bitmap:t}of p)!e||t||a.has(e)||a.add(e);const{isOffscreenCanvasSupported:s}=this.evaluatorOptions;if(a.size>0){const e=p.slice();for(const[t,i]of r)t.startsWith(u)&&i.bitmap&&a.has(i.bitmapId)&&e.push(i);t=AnnotationFactory.generateImages(e,this.xref,s)}else t=AnnotationFactory.generateImages(p,this.xref,s);y=new RefSet;m=Promise.all([e,this.#O(p,y,null)]).then((([e])=>e?AnnotationFactory.printNewAnnotations(e,d,i,p,t):null))}const w=Promise.all([C,E]).then((([r])=>{const n=new OperatorList(a,t);e.send("StartRenderPage",{transparency:d.hasBlendModes(this.resources,this.nonBlendModesSet),pageIndex:this.pageIndex,cacheKey:s});return d.getOperatorList({stream:r,task:i,resources:this.resources,operatorList:n}).then((function(){return n}))}));return Promise.all([w,this._parsedAnnotations,m]).then((function([e,t,s]){if(s){t=t.filter((e=>!(e.ref&&y.has(e.ref))));for(let e=0,i=s.length;e<i;e++){const a=s[e];if(a.refToReplace){const r=t.findIndex((e=>e.ref&&isRefsEqual(e.ref,a.refToReplace)));if(r>=0){t.splice(r,1,a);s.splice(e--,1);i--}}}t=t.concat(s)}if(0===t.length||a&l){e.flush(!0);return{length:e.totalLength}}const C=!!(a&h),E=!!(a&Q),u=!!(a&g),f=!!(a&o),p=!!(a&c),m=[];for(const e of t)(u||f&&e.mustBeViewed(r,C)&&e.mustBeViewedWhenEditing(E,n)||p&&e.mustBePrinted(r))&&m.push(e.getOperatorList(d,i,a,r).catch((function(e){warn(`getOperatorList - ignoring annotation data during "${i.name}" task: "${e}".`);return{opList:null,separateForm:!1,separateCanvas:!1}})));return Promise.all(m).then((function(t){let i=!1,a=!1;for(const{opList:s,separateForm:r,separateCanvas:n}of t){e.addOpList(s);i||=r;a||=n}e.flush(!0,{form:i,canvas:a});return{length:e.totalLength}}))}))}async extractTextContent({handler:e,task:t,includeMarkedContent:i,disableNormalization:a,sink:s}){const r=this.getContentStream(),n=this.loadResources(["ExtGState","Font","Properties","XObject"]),g=this.pdfManager.ensureCatalog("lang"),[o,,c]=await Promise.all([r,n,g]);return new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}).getTextContent({stream:o,task:t,resources:this.resources,includeMarkedContent:i,disableNormalization:a,sink:s,viewBox:this.view,lang:c})}async getStructTree(){const e=await this.pdfManager.ensureCatalog("structTreeRoot");if(!e)return null;await this._parsedAnnotations;return(await this.pdfManager.ensure(this,"_parseStructTree",[e])).serializable}_parseStructTree(e){const t=new StructTreePage(e,this.pageDict);t.parse(this.ref);return t}async getAnnotationsData(e,t,i){const a=await this._parsedAnnotations;if(0===a.length)return a;const s=[],r=[];let n;const C=!!(i&g),h=!!(i&o),l=!!(i&c);for(const i of a){const a=C||h&&i.viewable;(a||l&&i.printable)&&s.push(i.data);if(i.hasTextContent&&a){n||=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});r.push(i.extractTextContent(n,t,[-1/0,-1/0,1/0,1/0]).catch((function(e){warn(`getAnnotationsData - ignoring textContent during "${t.name}" task: "${e}".`)})))}}await Promise.all(r);return s}get annotations(){const e=this._getInheritableProperty("Annots");return shadow(this,"annotations",Array.isArray(e)?e:[])}get _parsedAnnotations(){return shadow(this,"_parsedAnnotations",this.pdfManager.ensure(this,"annotations").then((async e=>{if(0===e.length)return e;const[t,i]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureDoc("fieldObjects")]);if(!t)return[];const a=i?.orphanFields,s=[];for(const i of e)s.push(AnnotationFactory.create(this.xref,i,t,this._localIdFactory,!1,a,this.ref).catch((function(e){warn(`_parsedAnnotations: "${e}".`);return null})));const r=[];let n,g;for(const e of await Promise.all(s))e&&(e instanceof WidgetAnnotation?(g||=[]).push(e):e instanceof PopupAnnotation?(n||=[]).push(e):r.push(e));g&&r.push(...g);n&&r.push(...n);return r})))}get jsActions(){return shadow(this,"jsActions",collectActions(this.xref,this.pageDict,pA))}}const Ig=new Uint8Array([37,80,68,70,45]),cg=new Uint8Array([115,116,97,114,116,120,114,101,102]),Cg=new Uint8Array([101,110,100,111,98,106]);function find(e,t,i=1024,a=!1){const s=t.length,r=e.peekBytes(i),n=r.length-s;if(n<=0)return!1;if(a){const i=s-1;let a=r.length-1;for(;a>=i;){let n=0;for(;n<s&&r[a-n]===t[i-n];)n++;if(n>=s){e.pos+=a-i;return!0}a--}}else{let i=0;for(;i<=n;){let a=0;for(;a<s&&r[i+a]===t[a];)a++;if(a>=s){e.pos+=i;return!0}i++}}return!1}class PDFDocument{constructor(e,t){if(t.length<=0)throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");this.pdfManager=e;this.stream=t;this.xref=new XRef(t,e);this._pagePromises=new Map;this._version=null;const i={font:0};this._globalIdFactory=class{static getDocId(){return`g_${e.docId}`}static createFontId(){return"f"+ ++i.font}static createObjId(){unreachable("Abstract method `createObjId` called.")}static getPageObjId(){unreachable("Abstract method `getPageObjId` called.")}}}parse(e){this.xref.parse(e);this.catalog=new Catalog(this.pdfManager,this.xref)}get linearization(){let e=null;try{e=Linearization.create(this.stream)}catch(e){if(e instanceof MissingDataException)throw e;info(e)}return shadow(this,"linearization",e)}get startXRef(){const e=this.stream;let t=0;if(this.linearization){e.reset();if(find(e,Cg)){e.skip(6);let i=e.peekByte();for(;isWhiteSpace(i);){e.pos++;i=e.peekByte()}t=e.pos-e.start}}else{const i=1024,a=cg.length;let s=!1,r=e.end;for(;!s&&r>0;){r-=i-a;r<0&&(r=0);e.pos=r;s=find(e,cg,i,!0)}if(s){e.skip(9);let i;do{i=e.getByte()}while(isWhiteSpace(i));let a="";for(;i>=32&&i<=57;){a+=String.fromCharCode(i);i=e.getByte()}t=parseInt(a,10);isNaN(t)&&(t=0)}}return shadow(this,"startXRef",t)}checkHeader(){const e=this.stream;e.reset();if(!find(e,Ig))return;e.moveStart();e.skip(Ig.length);let t,i="";for(;(t=e.getByte())>32&&i.length<7;)i+=String.fromCharCode(t);kt.test(i)?this._version=i:warn(`Invalid PDF header version: ${i}`)}parseStartXRef(){this.xref.setStartXRef(this.startXRef)}get numPages(){let e=0;e=this.catalog.hasActualNumPages?this.catalog.numPages:this.xfaFactory?this.xfaFactory.getNumPages():this.linearization?this.linearization.numPages:this.catalog.numPages;return shadow(this,"numPages",e)}_hasOnlyDocumentSignatures(e,t=0){return!!Array.isArray(e)&&e.every((e=>{if(!((e=this.xref.fetchIfRef(e))instanceof Dict))return!1;if(e.has("Kids")){if(++t>10){warn("_hasOnlyDocumentSignatures: maximum recursion depth reached");return!1}return this._hasOnlyDocumentSignatures(e.get("Kids"),t)}const i=isName(e.get("FT"),"Sig"),a=e.get("Rect"),s=Array.isArray(a)&&a.every((e=>0===e));return i&&s}))}get _xfaStreams(){const e=this.catalog.acroForm;if(!e)return null;const t=e.get("XFA"),i={"xdp:xdp":"",template:"",datasets:"",config:"",connectionSet:"",localeSet:"",stylesheet:"","/xdp:xdp":""};if(t instanceof BaseStream&&!t.isEmpty){i["xdp:xdp"]=t;return i}if(!Array.isArray(t)||0===t.length)return null;for(let e=0,a=t.length;e<a;e+=2){let s;s=0===e?"xdp:xdp":e===a-2?"/xdp:xdp":t[e];if(!i.hasOwnProperty(s))continue;const r=this.xref.fetchIfRef(t[e+1]);r instanceof BaseStream&&!r.isEmpty&&(i[s]=r)}return i}get xfaDatasets(){const e=this._xfaStreams;if(!e)return shadow(this,"xfaDatasets",null);for(const t of["datasets","xdp:xdp"]){const i=e[t];if(i)try{const e=stringToUTF8String(i.getString());return shadow(this,"xfaDatasets",new DatasetReader({[t]:e}))}catch{warn("XFA - Invalid utf-8 string.");break}}return shadow(this,"xfaDatasets",null)}get xfaData(){const e=this._xfaStreams;if(!e)return null;const t=Object.create(null);for(const[i,a]of Object.entries(e))if(a)try{t[i]=stringToUTF8String(a.getString())}catch{warn("XFA - Invalid utf-8 string.");return null}return t}get xfaFactory(){let e;this.pdfManager.enableXfa&&this.catalog.needsRendering&&this.formInfo.hasXfa&&!this.formInfo.hasAcroForm&&(e=this.xfaData);return shadow(this,"xfaFactory",e?new XFAFactory(e):null)}get isPureXfa(){return!!this.xfaFactory&&this.xfaFactory.isValid()}get htmlForXfa(){return this.xfaFactory?this.xfaFactory.getPages():null}async loadXfaImages(){const e=await this.pdfManager.ensureCatalog("xfaImages");if(!e)return;const t=e.getKeys(),i=new ObjectLoader(e,t,this.xref);await i.load();const a=new Map;for(const i of t){const t=e.get(i);t instanceof BaseStream&&a.set(i,t.getBytes())}this.xfaFactory.setImages(a)}async loadXfaFonts(e,t){const i=await this.pdfManager.ensureCatalog("acroForm");if(!i)return;const a=await i.getAsync("DR");if(!(a instanceof Dict))return;const s=new ObjectLoader(a,["Font"],this.xref);await s.load();const r=a.get("Font");if(!(r instanceof Dict))return;const n=Object.assign(Object.create(null),this.pdfManager.evaluatorOptions);n.useSystemFonts=!1;const g=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:-1,idFactory:this._globalIdFactory,fontCache:this.catalog.fontCache,builtInCMapCache:this.catalog.builtInCMapCache,standardFontDataCache:this.catalog.standardFontDataCache,options:n}),o=new OperatorList,c=[],C={get font(){return c.at(-1)},set font(e){c.push(e)},clone(){return this}},h=new Map;r.forEach(((e,t)=>{h.set(e,t)}));const l=[];for(const[e,i]of h){const s=i.get("FontDescriptor");if(!(s instanceof Dict))continue;let r=s.get("FontFamily");r=r.replaceAll(/[ ]+(\d)/g,"$1");const n={fontFamily:r,fontWeight:s.get("FontWeight"),italicAngle:-s.get("ItalicAngle")};validateCSSFont(n)&&l.push(g.handleSetFont(a,[Name.get(e),1],null,o,t,C,null,n).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(l);const Q=this.xfaFactory.setFonts(c);if(!Q)return;n.ignoreErrors=!0;l.length=0;c.length=0;const E=new Set;for(const e of Q)getXfaFontName(`${e}-Regular`)||E.add(e);E.size&&Q.push("PdfJS-Fallback");for(const e of Q)if(!E.has(e))for(const i of[{name:"Regular",fontWeight:400,italicAngle:0},{name:"Bold",fontWeight:700,italicAngle:0},{name:"Italic",fontWeight:400,italicAngle:12},{name:"BoldItalic",fontWeight:700,italicAngle:12}]){const s=`${e}-${i.name}`,r=getXfaFontDict(s);l.push(g.handleSetFont(a,[Name.get(s),1],null,o,t,C,r,{fontFamily:e,fontWeight:i.fontWeight,italicAngle:i.italicAngle}).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(l);this.xfaFactory.appendFonts(c,E)}async serializeXfaData(e){return this.xfaFactory?this.xfaFactory.serializeData(e):null}get version(){return this.catalog.version||this._version}get formInfo(){const e={hasFields:!1,hasAcroForm:!1,hasXfa:!1,hasSignatures:!1},t=this.catalog.acroForm;if(!t)return shadow(this,"formInfo",e);try{const i=t.get("Fields"),a=Array.isArray(i)&&i.length>0;e.hasFields=a;const s=t.get("XFA");e.hasXfa=Array.isArray(s)&&s.length>0||s instanceof BaseStream&&!s.isEmpty;const r=!!(1&t.get("SigFlags")),n=r&&this._hasOnlyDocumentSignatures(i);e.hasAcroForm=a&&!n;e.hasSignatures=r}catch(e){if(e instanceof MissingDataException)throw e;warn(`Cannot fetch form information: "${e}".`)}return shadow(this,"formInfo",e)}get documentInfo(){const e={PDFFormatVersion:this.version,Language:this.catalog.lang,EncryptFilterName:this.xref.encrypt?this.xref.encrypt.filterName:null,IsLinearized:!!this.linearization,IsAcroFormPresent:this.formInfo.hasAcroForm,IsXFAPresent:this.formInfo.hasXfa,IsCollectionPresent:!!this.catalog.collection,IsSignaturesPresent:this.formInfo.hasSignatures};let t;try{t=this.xref.trailer.get("Info")}catch(e){if(e instanceof MissingDataException)throw e;info("The document information dictionary is invalid.")}if(!(t instanceof Dict))return shadow(this,"documentInfo",e);for(const i of t.getKeys()){const a=t.get(i);switch(i){case"Title":case"Author":case"Subject":case"Keywords":case"Creator":case"Producer":case"CreationDate":case"ModDate":if("string"==typeof a){e[i]=stringToPDFString(a);continue}break;case"Trapped":if(a instanceof Name){e[i]=a;continue}break;default:let t;switch(typeof a){case"string":t=stringToPDFString(a);break;case"number":case"boolean":t=a;break;default:a instanceof Name&&(t=a)}if(void 0===t){warn(`Bad value, for custom key "${i}", in Info: ${a}.`);continue}e.Custom||(e.Custom=Object.create(null));e.Custom[i]=t;continue}warn(`Bad value, for key "${i}", in Info: ${a}.`)}return shadow(this,"documentInfo",e)}get fingerprints(){function validate(e){return"string"==typeof e&&e.length>0&&"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"!==e}function hexString(e){const t=[];for(const i of e){const e=i.toString(16);t.push(e.padStart(2,"0"))}return t.join("")}const e=this.xref.trailer.get("ID");let t,i;if(Array.isArray(e)&&validate(e[0])){t=stringToBytes(e[0]);e[1]!==e[0]&&validate(e[1])&&(i=stringToBytes(e[1]))}else t=Ks(this.stream.getByteRange(0,1024),0,1024);return shadow(this,"fingerprints",[hexString(t),i?hexString(i):null])}async _getLinearizationPage(e){const{catalog:t,linearization:i,xref:a}=this,s=Ref.get(i.objectNumberFirst,0);try{const e=await a.fetchAsync(s);if(e instanceof Dict){let i=e.getRaw("Type");i instanceof Ref&&(i=await a.fetchAsync(i));if(isName(i,"Page")||!e.has("Type")&&!e.has("Kids")&&e.has("Contents")){t.pageKidsCountCache.has(s)||t.pageKidsCountCache.put(s,1);t.pageIndexCache.has(s)||t.pageIndexCache.put(s,0);return[e,s]}}throw new FormatError("The Linearization dictionary doesn't point to a valid Page dictionary.")}catch(i){warn(`_getLinearizationPage: "${i.message}".`);return t.getPageDict(e)}}getPage(e){const t=this._pagePromises.get(e);if(t)return t;const{catalog:i,linearization:a,xfaFactory:s}=this;let r;r=s?Promise.resolve([Dict.empty,null]):a?.pageFirst===e?this._getLinearizationPage(e):i.getPageDict(e);r=r.then((([t,a])=>new Page({pdfManager:this.pdfManager,xref:this.xref,pageIndex:e,pageDict:t,ref:a,globalIdFactory:this._globalIdFactory,fontCache:i.fontCache,builtInCMapCache:i.builtInCMapCache,standardFontDataCache:i.standardFontDataCache,globalImageCache:i.globalImageCache,systemFontCache:i.systemFontCache,nonBlendModesSet:i.nonBlendModesSet,xfaFactory:s})));this._pagePromises.set(e,r);return r}async checkFirstPage(e=!1){if(!e)try{await this.getPage(0)}catch(e){if(e instanceof XRefEntryException){this._pagePromises.delete(0);await this.cleanup();throw new XRefParseException}}}async checkLastPage(e=!1){const{catalog:t,pdfManager:i}=this;t.setActualNumPages();let a;try{await Promise.all([i.ensureDoc("xfaFactory"),i.ensureDoc("linearization"),i.ensureCatalog("numPages")]);if(this.xfaFactory)return;a=this.linearization?this.linearization.numPages:t.numPages;if(!Number.isInteger(a))throw new FormatError("Page count is not an integer.");if(a<=1)return;await this.getPage(a-1)}catch(s){this._pagePromises.delete(a-1);await this.cleanup();if(s instanceof XRefEntryException&&!e)throw new XRefParseException;warn(`checkLastPage - invalid /Pages tree /Count: ${a}.`);let r;try{r=await t.getAllPageDicts(e)}catch(i){if(i instanceof XRefEntryException&&!e)throw new XRefParseException;t.setActualNumPages(1);return}for(const[e,[a,s]]of r){let r;if(a instanceof Error){r=Promise.reject(a);r.catch((()=>{}))}else r=Promise.resolve(new Page({pdfManager:i,xref:this.xref,pageIndex:e,pageDict:a,ref:s,globalIdFactory:this._globalIdFactory,fontCache:t.fontCache,builtInCMapCache:t.builtInCMapCache,standardFontDataCache:t.standardFontDataCache,globalImageCache:t.globalImageCache,systemFontCache:t.systemFontCache,nonBlendModesSet:t.nonBlendModesSet,xfaFactory:null}));this._pagePromises.set(e,r)}t.setActualNumPages(r.size)}}fontFallback(e,t){return this.catalog.fontFallback(e,t)}async cleanup(e=!1){return this.catalog?this.catalog.cleanup(e):clearGlobalCaches()}async#P(e,t,i,a,s,r,n){const{xref:g}=this;if(!(i instanceof Ref)||r.has(i))return;r.put(i);const o=await g.fetchAsync(i);if(!(o instanceof Dict))return;if(o.has("T")){const t=stringToPDFString(await o.getAsync("T"));e=""===e?t:`${e}.${t}`}else{let i=o;for(;;){i=i.getRaw("Parent")||t;if(i instanceof Ref){if(r.has(i))break;i=await g.fetchAsync(i)}if(!(i instanceof Dict))break;if(i.has("T")){const t=stringToPDFString(await i.getAsync("T"));e=""===e?t:`${e}.${t}`;break}}}t&&!o.has("Parent")&&isName(o.get("Subtype"),"Widget")&&n.put(i,t);a.has(e)||a.set(e,[]);a.get(e).push(AnnotationFactory.create(g,i,s,null,!0,n,null).then((e=>e?.getFieldObject())).catch((function(e){warn(`#collectFieldObjects: "${e}".`);return null})));if(!o.has("Kids"))return;const c=await o.getAsync("Kids");if(Array.isArray(c))for(const t of c)await this.#P(e,i,t,a,s,r,n)}get fieldObjects(){if(!this.formInfo.hasFields)return shadow(this,"fieldObjects",Promise.resolve(null));return shadow(this,"fieldObjects",Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureCatalog("acroForm")]).then((async([e,t])=>{if(!e)return null;const i=new RefSet,a=Object.create(null),s=new Map,r=new RefSetCache;for(const a of await t.getAsync("Fields"))await this.#P("",null,a,s,e,i,r);const n=[];for(const[e,t]of s)n.push(Promise.all(t).then((t=>{(t=t.filter((e=>!!e))).length>0&&(a[e]=t)})));await Promise.all(n);return{allFields:a,orphanFields:r}})))}get hasJSActions(){return shadow(this,"hasJSActions",this.pdfManager.ensureDoc("_parseHasJSActions"))}async _parseHasJSActions(){const[e,t]=await Promise.all([this.pdfManager.ensureCatalog("jsActions"),this.pdfManager.ensureDoc("fieldObjects")]);return!!e||!!t&&Object.values(t.allFields).some((e=>e.some((e=>null!==e.actions))))}get calculationOrderIds(){const e=this.catalog.acroForm;if(!e?.has("CO"))return shadow(this,"calculationOrderIds",null);const t=e.get("CO");if(!Array.isArray(t)||0===t.length)return shadow(this,"calculationOrderIds",null);const i=[];for(const e of t)e instanceof Ref&&i.push(e.toString());return 0===i.length?shadow(this,"calculationOrderIds",null):shadow(this,"calculationOrderIds",i)}get annotationGlobals(){return shadow(this,"annotationGlobals",AnnotationFactory.createGlobals(this.pdfManager))}}class BasePdfManager{constructor(e){this._docBaseUrl=function parseDocBaseUrl(e){if(e){const t=createValidAbsoluteUrl(e);if(t)return t.href;warn(`Invalid absolute docBaseUrl: "${e}".`)}return null}(e.docBaseUrl);this._docId=e.docId;this._password=e.password;this.enableXfa=e.enableXfa;e.evaluatorOptions.isOffscreenCanvasSupported&&=FeatureTest.isOffscreenCanvasSupported;this.evaluatorOptions=Object.freeze(e.evaluatorOptions)}get docId(){return this._docId}get password(){return this._password}get docBaseUrl(){return this._docBaseUrl}get catalog(){return this.pdfDocument.catalog}ensureDoc(e,t){return this.ensure(this.pdfDocument,e,t)}ensureXRef(e,t){return this.ensure(this.pdfDocument.xref,e,t)}ensureCatalog(e,t){return this.ensure(this.pdfDocument.catalog,e,t)}getPage(e){return this.pdfDocument.getPage(e)}fontFallback(e,t){return this.pdfDocument.fontFallback(e,t)}loadXfaFonts(e,t){return this.pdfDocument.loadXfaFonts(e,t)}loadXfaImages(){return this.pdfDocument.loadXfaImages()}serializeXfaData(e){return this.pdfDocument.serializeXfaData(e)}cleanup(e=!1){return this.pdfDocument.cleanup(e)}async ensure(e,t,i){unreachable("Abstract method `ensure` called")}requestRange(e,t){unreachable("Abstract method `requestRange` called")}requestLoadedStream(e=!1){unreachable("Abstract method `requestLoadedStream` called")}sendProgressiveData(e){unreachable("Abstract method `sendProgressiveData` called")}updatePassword(e){this._password=e}terminate(e){unreachable("Abstract method `terminate` called")}}class LocalPdfManager extends BasePdfManager{constructor(e){super(e);const t=new Stream(e.source);this.pdfDocument=new PDFDocument(this,t);this._loadedStreamPromise=Promise.resolve(t)}async ensure(e,t,i){const a=e[t];return"function"==typeof a?a.apply(e,i):a}requestRange(e,t){return Promise.resolve()}requestLoadedStream(e=!1){return this._loadedStreamPromise}terminate(e){}}class NetworkPdfManager extends BasePdfManager{constructor(e){super(e);this.streamManager=new ChunkedStreamManager(e.source,{msgHandler:e.handler,length:e.length,disableAutoFetch:e.disableAutoFetch,rangeChunkSize:e.rangeChunkSize});this.pdfDocument=new PDFDocument(this,this.streamManager.getStream())}async ensure(e,t,i){try{const a=e[t];return"function"==typeof a?a.apply(e,i):a}catch(a){if(!(a instanceof MissingDataException))throw a;await this.requestRange(a.begin,a.end);return this.ensure(e,t,i)}}requestRange(e,t){return this.streamManager.requestRange(e,t)}requestLoadedStream(e=!1){return this.streamManager.requestAllChunks(e)}sendProgressiveData(e){this.streamManager.onReceiveData({chunk:e})}terminate(e){this.streamManager.abort(e)}}const hg=1,Bg=2,lg=1,Qg=2,Eg=3,ug=4,dg=5,fg=6,pg=7,mg=8;function wrapReason(e){e instanceof Error||"object"==typeof e&&null!==e||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(e.name){case"AbortException":return new AbortException(e.message);case"MissingPDFException":return new MissingPDFException(e.message);case"PasswordException":return new PasswordException(e.message,e.code);case"UnexpectedResponseException":return new UnexpectedResponseException(e.message,e.status);case"UnknownErrorException":return new UnknownErrorException(e.message,e.details);default:return new UnknownErrorException(e.message,e.toString())}}class MessageHandler{constructor(e,t,i){this.sourceName=e;this.targetName=t;this.comObj=i;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);this._onComObjOnMessage=e=>{const t=e.data;if(t.targetName!==this.sourceName)return;if(t.stream){this.#W(t);return}if(t.callback){const e=t.callbackId,i=this.callbackCapabilities[e];if(!i)throw new Error(`Cannot resolve callback ${e}`);delete this.callbackCapabilities[e];if(t.callback===hg)i.resolve(t.data);else{if(t.callback!==Bg)throw new Error("Unexpected callback case");i.reject(wrapReason(t.reason))}return}const a=this.actionHandler[t.action];if(!a)throw new Error(`Unknown action from worker: ${t.action}`);if(t.callbackId){const e=this.sourceName,s=t.sourceName;new Promise((function(e){e(a(t.data))})).then((function(a){i.postMessage({sourceName:e,targetName:s,callback:hg,callbackId:t.callbackId,data:a})}),(function(a){i.postMessage({sourceName:e,targetName:s,callback:Bg,callbackId:t.callbackId,reason:wrapReason(a)})}))}else t.streamId?this.#j(t):a(t.data)};i.addEventListener("message",this._onComObjOnMessage)}on(e,t){const i=this.actionHandler;if(i[e])throw new Error(`There is already an actionName called "${e}"`);i[e]=t}send(e,t,i){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,data:t},i)}sendWithPromise(e,t,i){const a=this.callbackId++,s=Promise.withResolvers();this.callbackCapabilities[a]=s;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,callbackId:a,data:t},i)}catch(e){s.reject(e)}return s.promise}sendWithStream(e,t,i,a){const s=this.streamId++,r=this.sourceName,n=this.targetName,g=this.comObj;return new ReadableStream({start:i=>{const o=Promise.withResolvers();this.streamControllers[s]={controller:i,startCall:o,pullCall:null,cancelCall:null,isClosed:!1};g.postMessage({sourceName:r,targetName:n,action:e,streamId:s,data:t,desiredSize:i.desiredSize},a);return o.promise},pull:e=>{const t=Promise.withResolvers();this.streamControllers[s].pullCall=t;g.postMessage({sourceName:r,targetName:n,stream:fg,streamId:s,desiredSize:e.desiredSize});return t.promise},cancel:e=>{assert(e instanceof Error,"cancel must have a valid reason");const t=Promise.withResolvers();this.streamControllers[s].cancelCall=t;this.streamControllers[s].isClosed=!0;g.postMessage({sourceName:r,targetName:n,stream:lg,streamId:s,reason:wrapReason(e)});return t.promise}},i)}#j(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this,n=this.actionHandler[e.action],g={enqueue(e,r=1,n){if(this.isCancelled)return;const g=this.desiredSize;this.desiredSize-=r;if(g>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}s.postMessage({sourceName:i,targetName:a,stream:ug,streamId:t,chunk:e},n)},close(){if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:Eg,streamId:t});delete r.streamSinks[t]}},error(e){assert(e instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:dg,streamId:t,reason:wrapReason(e)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:e.desiredSize,ready:null};g.sinkCapability.resolve();g.ready=g.sinkCapability.promise;this.streamSinks[t]=g;new Promise((function(t){t(n(e.data,g))})).then((function(){s.postMessage({sourceName:i,targetName:a,stream:mg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:mg,streamId:t,reason:wrapReason(e)})}))}#W(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this.streamControllers[t],n=this.streamSinks[t];switch(e.stream){case mg:e.success?r.startCall.resolve():r.startCall.reject(wrapReason(e.reason));break;case pg:e.success?r.pullCall.resolve():r.pullCall.reject(wrapReason(e.reason));break;case fg:if(!n){s.postMessage({sourceName:i,targetName:a,stream:pg,streamId:t,success:!0});break}n.desiredSize<=0&&e.desiredSize>0&&n.sinkCapability.resolve();n.desiredSize=e.desiredSize;new Promise((function(e){e(n.onPull?.())})).then((function(){s.postMessage({sourceName:i,targetName:a,stream:pg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:pg,streamId:t,reason:wrapReason(e)})}));break;case ug:assert(r,"enqueue should have stream controller");if(r.isClosed)break;r.controller.enqueue(e.chunk);break;case Eg:assert(r,"close should have stream controller");if(r.isClosed)break;r.isClosed=!0;r.controller.close();this.#X(r,t);break;case dg:assert(r,"error should have stream controller");r.controller.error(wrapReason(e.reason));this.#X(r,t);break;case Qg:e.success?r.cancelCall.resolve():r.cancelCall.reject(wrapReason(e.reason));this.#X(r,t);break;case lg:if(!n)break;new Promise((function(t){t(n.onCancel?.(wrapReason(e.reason)))})).then((function(){s.postMessage({sourceName:i,targetName:a,stream:Qg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:Qg,streamId:t,reason:wrapReason(e)})}));n.sinkCapability.reject(wrapReason(e.reason));n.isCancelled=!0;delete this.streamSinks[t];break;default:throw new Error("Unexpected stream case")}}async#X(e,t){await Promise.allSettled([e.startCall?.promise,e.pullCall?.promise,e.cancelCall?.promise]);delete this.streamControllers[t]}destroy(){this.comObj.removeEventListener("message",this._onComObjOnMessage)}}class PDFWorkerStream{constructor(e){this._msgHandler=e;this._contentLength=null;this._fullRequestReader=null;this._rangeRequestReaders=[]}getFullReader(){assert(!this._fullRequestReader,"PDFWorkerStream.getFullReader can only be called once.");this._fullRequestReader=new PDFWorkerStreamReader(this._msgHandler);return this._fullRequestReader}getRangeReader(e,t){const i=new PDFWorkerStreamRangeReader(e,t,this._msgHandler);this._rangeRequestReaders.push(i);return i}cancelAllRequests(e){this._fullRequestReader?.cancel(e);for(const t of this._rangeRequestReaders.slice(0))t.cancel(e)}}class PDFWorkerStreamReader{constructor(e){this._msgHandler=e;this.onProgress=null;this._contentLength=null;this._isRangeSupported=!1;this._isStreamingSupported=!1;const t=this._msgHandler.sendWithStream("GetReader");this._reader=t.getReader();this._headersReady=this._msgHandler.sendWithPromise("ReaderHeadersReady").then((e=>{this._isStreamingSupported=e.isStreamingSupported;this._isRangeSupported=e.isRangeSupported;this._contentLength=e.contentLength}))}get headersReady(){return this._headersReady}get contentLength(){return this._contentLength}get isStreamingSupported(){return this._isStreamingSupported}get isRangeSupported(){return this._isRangeSupported}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class PDFWorkerStreamRangeReader{constructor(e,t,i){this._msgHandler=i;this.onProgress=null;const a=this._msgHandler.sendWithStream("GetRangeReader",{begin:e,end:t});this._reader=a.getReader()}get isStreamingSupported(){return!1}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class WorkerTask{constructor(e){this.name=e;this.terminated=!1;this._capability=Promise.withResolvers()}get finished(){return this._capability.promise}finish(){this._capability.resolve()}terminate(){this.terminated=!0}ensureNotTerminated(){if(this.terminated)throw new Error("Worker task was terminated")}}class WorkerMessageHandler{static setup(e,t){let i=!1;e.on("test",(function(t){if(!i){i=!0;e.send("test",t instanceof Uint8Array)}}));e.on("configure",(function(e){!function setVerbosityLevel(e){Number.isInteger(e)&&(gt=e)}(e.verbosity)}));e.on("GetDocRequest",(function(e){return WorkerMessageHandler.createDocumentHandler(e,t)}))}static createDocumentHandler(e,t){let i,a=!1,s=null;const r=new Set,n=getVerbosityLevel(),{docId:g,apiVersion:o}=e,c="4.7.76";if(o!==c)throw new Error(`The API version "${o}" does not match the Worker version "${c}".`);const C=[];for(const e in[])C.push(e);if(C.length)throw new Error("The `Array.prototype` contains unexpected enumerable properties: "+C.join(", ")+"; thus breaking e.g. `for...in` iteration of `Array`s.");const h=g+"_worker";let l=new MessageHandler(h,g,t);function ensureNotTerminated(){if(a)throw new Error("Worker was terminated")}function startWorkerTask(e){r.add(e)}function finishWorkerTask(e){e.finish();r.delete(e)}async function loadDocument(e){await i.ensureDoc("checkHeader");await i.ensureDoc("parseStartXRef");await i.ensureDoc("parse",[e]);await i.ensureDoc("checkFirstPage",[e]);await i.ensureDoc("checkLastPage",[e]);const t=await i.ensureDoc("isPureXfa");if(t){const e=new WorkerTask("loadXfaFonts");startWorkerTask(e);await Promise.all([i.loadXfaFonts(l,e).catch((e=>{})).then((()=>finishWorkerTask(e))),i.loadXfaImages()])}const[a,s]=await Promise.all([i.ensureDoc("numPages"),i.ensureDoc("fingerprints")]);return{numPages:a,fingerprints:s,htmlForXfa:t?await i.ensureDoc("htmlForXfa"):null}}function getPdfManager({data:e,password:t,disableAutoFetch:i,rangeChunkSize:a,length:r,docBaseUrl:n,enableXfa:o,evaluatorOptions:c}){const C={source:null,disableAutoFetch:i,docBaseUrl:n,docId:g,enableXfa:o,evaluatorOptions:c,handler:l,length:r,password:t,rangeChunkSize:a},h=Promise.withResolvers();let Q;if(e){try{C.source=e;Q=new LocalPdfManager(C);h.resolve(Q)}catch(e){h.reject(e)}return h.promise}let E,u=[];try{E=new PDFWorkerStream(l)}catch(e){h.reject(e);return h.promise}const d=E.getFullReader();d.headersReady.then((function(){if(d.isRangeSupported){C.source=E;C.length=d.contentLength;C.disableAutoFetch||=d.isStreamingSupported;Q=new NetworkPdfManager(C);for(const e of u)Q.sendProgressiveData(e);u=[];h.resolve(Q);s=null}})).catch((function(e){h.reject(e);s=null}));let f=0;new Promise((function(e,t){const readChunk=function({value:e,done:i}){try{ensureNotTerminated();if(i){Q||function(){const e=arrayBuffersToBytes(u);r&&e.length!==r&&warn("reported HTTP length is different from actual");try{C.source=e;Q=new LocalPdfManager(C);h.resolve(Q)}catch(e){h.reject(e)}u=[]}();s=null;return}f+=e.byteLength;d.isStreamingSupported||l.send("DocProgress",{loaded:f,total:Math.max(f,d.contentLength||0)});Q?Q.sendProgressiveData(e):u.push(e);d.read().then(readChunk,t)}catch(e){t(e)}};d.read().then(readChunk,t)})).catch((function(e){h.reject(e);s=null}));s=function(e){E.cancelAllRequests(e)};return h.promise}l.on("GetPage",(function(e){return i.getPage(e.pageIndex).then((function(e){return Promise.all([i.ensure(e,"rotate"),i.ensure(e,"ref"),i.ensure(e,"userUnit"),i.ensure(e,"view")]).then((function([e,t,i,a]){return{rotate:e,ref:t,refStr:t?.toString()??null,userUnit:i,view:a}}))}))}));l.on("GetPageIndex",(function(e){const t=Ref.get(e.num,e.gen);return i.ensureCatalog("getPageIndex",[t])}));l.on("GetDestinations",(function(e){return i.ensureCatalog("destinations")}));l.on("GetDestination",(function(e){return i.ensureCatalog("getDestination",[e.id])}));l.on("GetPageLabels",(function(e){return i.ensureCatalog("pageLabels")}));l.on("GetPageLayout",(function(e){return i.ensureCatalog("pageLayout")}));l.on("GetPageMode",(function(e){return i.ensureCatalog("pageMode")}));l.on("GetViewerPreferences",(function(e){return i.ensureCatalog("viewerPreferences")}));l.on("GetOpenAction",(function(e){return i.ensureCatalog("openAction")}));l.on("GetAttachments",(function(e){return i.ensureCatalog("attachments")}));l.on("GetDocJSActions",(function(e){return i.ensureCatalog("jsActions")}));l.on("GetPageJSActions",(function({pageIndex:e}){return i.getPage(e).then((function(e){return i.ensure(e,"jsActions")}))}));l.on("GetOutline",(function(e){return i.ensureCatalog("documentOutline")}));l.on("GetOptionalContentConfig",(function(e){return i.ensureCatalog("optionalContentConfig")}));l.on("GetPermissions",(function(e){return i.ensureCatalog("permissions")}));l.on("GetMetadata",(function(e){return Promise.all([i.ensureDoc("documentInfo"),i.ensureCatalog("metadata")])}));l.on("GetMarkInfo",(function(e){return i.ensureCatalog("markInfo")}));l.on("GetData",(function(e){return i.requestLoadedStream().then((function(e){return e.bytes}))}));l.on("GetAnnotations",(function({pageIndex:e,intent:t}){return i.getPage(e).then((function(i){const a=new WorkerTask(`GetAnnotations: page ${e}`);startWorkerTask(a);return i.getAnnotationsData(l,a,t).then((e=>{finishWorkerTask(a);return e}),(e=>{finishWorkerTask(a);throw e}))}))}));l.on("GetFieldObjects",(function(e){return i.ensureDoc("fieldObjects").then((e=>e?.allFields||null))}));l.on("HasJSActions",(function(e){return i.ensureDoc("hasJSActions")}));l.on("GetCalculationOrderIds",(function(e){return i.ensureDoc("calculationOrderIds")}));l.on("SaveDocument",(async function({isPureXfa:e,numPages:t,annotationStorage:a,filename:s}){const r=[i.requestLoadedStream(),i.ensureCatalog("acroForm"),i.ensureCatalog("acroFormRef"),i.ensureDoc("startXRef"),i.ensureDoc("xref"),i.ensureDoc("linearization"),i.ensureCatalog("structTreeRoot")],n=[],g=e?null:getNewAnnotationsMap(a),[o,c,C,h,Q,E,u]=await Promise.all(r),d=Q.trailer.getRaw("Root")||null;let f;if(g){u?await u.canUpdateStructTree({pdfManager:i,xref:Q,newAnnotationsByPage:g})&&(f=u):await StructTreeRoot.canCreateStructureTree({catalogRef:d,pdfManager:i,newAnnotationsByPage:g})&&(f=null);const e=AnnotationFactory.generateImages(a.values(),Q,i.evaluatorOptions.isOffscreenCanvasSupported),t=void 0===f?n:[];for(const[a,s]of g)t.push(i.getPage(a).then((t=>{const i=new WorkerTask(`Save (editor): page ${a}`);return t.saveNewAnnotations(l,i,s,e).finally((function(){finishWorkerTask(i)}))})));null===f?n.push(Promise.all(t).then((async e=>{await StructTreeRoot.createStructureTree({newAnnotationsByPage:g,xref:Q,catalogRef:d,pdfManager:i,newRefs:e});return e}))):f&&n.push(Promise.all(t).then((async e=>{await f.updateStructureTree({newAnnotationsByPage:g,pdfManager:i,newRefs:e});return e})))}if(e)n.push(i.serializeXfaData(a));else for(let e=0;e<t;e++)n.push(i.getPage(e).then((function(t){const i=new WorkerTask(`Save: page ${e}`);return t.save(l,i,a).finally((function(){finishWorkerTask(i)}))})));const p=await Promise.all(n);let m=[],y=null;if(e){y=p[0];if(!y)return o.bytes}else{m=p.flat(2);if(0===m.length)return o.bytes}const w=C&&c instanceof Dict&&m.some((e=>e.needAppearances)),D=c instanceof Dict&&c.get("XFA")||null;let b=null,F=!1;if(Array.isArray(D)){for(let e=0,t=D.length;e<t;e+=2)if("datasets"===D[e]){b=D[e+1];F=!0}null===b&&(b=Q.getNewTemporaryRef())}else D&&warn("Unsupported XFA type.");let S=Object.create(null);if(Q.trailer){const e=Object.create(null),t=Q.trailer.get("Info")||null;t instanceof Dict&&t.forEach(((t,i)=>{"string"==typeof i&&(e[t]=stringToPDFString(i))}));S={rootRef:d,encryptRef:Q.trailer.getRaw("Encrypt")||null,newRef:Q.getNewTemporaryRef(),infoRef:Q.trailer.getRaw("Info")||null,info:e,fileIds:Q.trailer.get("ID")||null,startXRef:E?h:Q.lastXRefStreamPos??h,filename:s}}return incrementalUpdate({originalData:o.bytes,xrefInfo:S,newRefs:m,xref:Q,hasXfa:!!D,xfaDatasetsRef:b,hasXfaDatasetsEntry:F,needAppearances:w,acroFormRef:C,acroForm:c,xfaData:y,useXrefStream:isDict(Q.topDict,"XRef")}).finally((()=>{Q.resetNewTemporaryRef()}))}));l.on("GetOperatorList",(function(e,t){const a=e.pageIndex;i.getPage(a).then((function(i){const s=new WorkerTask(`GetOperatorList: page ${a}`);startWorkerTask(s);const r=n>=mA.INFOS?Date.now():0;i.getOperatorList({handler:l,sink:t,task:s,intent:e.intent,cacheKey:e.cacheKey,annotationStorage:e.annotationStorage,modifiedIds:e.modifiedIds}).then((function(e){finishWorkerTask(s);r&&info(`page=${a+1} - getOperatorList: time=${Date.now()-r}ms, len=${e.length}`);t.close()}),(function(e){finishWorkerTask(s);s.terminated||t.error(e)}))}))}));l.on("GetTextContent",(function(e,t){const{pageIndex:a,includeMarkedContent:s,disableNormalization:r}=e;i.getPage(a).then((function(e){const i=new WorkerTask("GetTextContent: page "+a);startWorkerTask(i);const g=n>=mA.INFOS?Date.now():0;e.extractTextContent({handler:l,task:i,sink:t,includeMarkedContent:s,disableNormalization:r}).then((function(){finishWorkerTask(i);g&&info(`page=${a+1} - getTextContent: time=`+(Date.now()-g)+"ms");t.close()}),(function(e){finishWorkerTask(i);i.terminated||t.error(e)}))}))}));l.on("GetStructTree",(function(e){return i.getPage(e.pageIndex).then((function(e){return i.ensure(e,"getStructTree")}))}));l.on("FontFallback",(function(e){return i.fontFallback(e.id,l)}));l.on("Cleanup",(function(e){return i.cleanup(!0)}));l.on("Terminate",(function(e){a=!0;const t=[];if(i){i.terminate(new AbortException("Worker was terminated."));const e=i.cleanup();t.push(e);i=null}else clearGlobalCaches();s&&s(new AbortException("Worker was terminated."));for(const e of r){t.push(e.finished);e.terminate()}return Promise.all(t).then((function(){l.destroy();l=null}))}));l.on("Ready",(function(t){!function setupDoc(e){function onSuccess(e){ensureNotTerminated();l.send("GetDoc",{pdfInfo:e})}function onFailure(e){ensureNotTerminated();if(e instanceof PasswordException){const t=new WorkerTask(`PasswordException: response ${e.code}`);startWorkerTask(t);l.sendWithPromise("PasswordRequest",e).then((function({password:e}){finishWorkerTask(t);i.updatePassword(e);pdfManagerReady()})).catch((function(){finishWorkerTask(t);l.send("DocException",e)}))}else e instanceof InvalidPDFException||e instanceof MissingPDFException||e instanceof UnexpectedResponseException||e instanceof UnknownErrorException?l.send("DocException",e):l.send("DocException",new UnknownErrorException(e.message,e.toString()))}function pdfManagerReady(){ensureNotTerminated();loadDocument(!1).then(onSuccess,(function(e){ensureNotTerminated();e instanceof XRefParseException?i.requestLoadedStream().then((function(){ensureNotTerminated();loadDocument(!0).then(onSuccess,onFailure)})):onFailure(e)}))}ensureNotTerminated();getPdfManager(e).then((function(e){if(a){e.terminate(new AbortException("Worker was terminated."));throw new Error("Worker was terminated")}i=e;i.requestLoadedStream(!0).then((e=>{l.send("DataLoaded",{length:e.bytes.byteLength})}))})).then(pdfManagerReady,onFailure)}(e);e=null}));return h}static initializeFromPort(e){const t=new MessageHandler("worker","main",e);WorkerMessageHandler.setup(t,e);t.send("ready",null)}}"undefined"==typeof window&&!t&&"undefined"!=typeof self&&function isMessagePort(e){return"function"==typeof e.postMessage&&"onmessage"in e}(self)&&WorkerMessageHandler.initializeFromPort(self);var yg=__webpack_exports__.WorkerMessageHandler;export{yg as WorkerMessageHandler}; \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76.js b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76.js new file mode 100644 index 0000000000..f32bf4469c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/pdf.js/pdfjs-4.7.76.js @@ -0,0 +1,21 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},__webpack_exports__ = globalThis.pdfjsLib = {};t.d(__webpack_exports__,{AbortException:()=>AbortException,AnnotationEditorLayer:()=>AnnotationEditorLayer,AnnotationEditorParamsType:()=>m,AnnotationEditorType:()=>g,AnnotationEditorUIManager:()=>AnnotationEditorUIManager,AnnotationLayer:()=>AnnotationLayer,AnnotationMode:()=>p,CMapCompressionType:()=>X,ColorPicker:()=>ColorPicker,DOMSVGFactory:()=>DOMSVGFactory,DrawLayer:()=>DrawLayer,FeatureTest:()=>util_FeatureTest,GlobalWorkerOptions:()=>GlobalWorkerOptions,ImageKind:()=>_,InvalidPDFException:()=>InvalidPDFException,MissingPDFException:()=>MissingPDFException,OPS:()=>K,OutputScale:()=>OutputScale,PDFDataRangeTransport:()=>PDFDataRangeTransport,PDFDateString:()=>PDFDateString,PDFWorker:()=>PDFWorker,PasswordResponses:()=>Y,PermissionFlag:()=>f,PixelsPerInch:()=>PixelsPerInch,RenderingCancelledException:()=>RenderingCancelledException,TextLayer:()=>TextLayer,UnexpectedResponseException:()=>UnexpectedResponseException,Util:()=>Util,VerbosityLevel:()=>q,XfaLayer:()=>XfaLayer,build:()=>Yt,createValidAbsoluteUrl:()=>createValidAbsoluteUrl,fetchData:()=>fetchData,getDocument:()=>getDocument,getFilenameFromUrl:()=>getFilenameFromUrl,getPdfFilenameFromUrl:()=>getPdfFilenameFromUrl,getXfaPageViewport:()=>getXfaPageViewport,isDataScheme:()=>isDataScheme,isPdfFile:()=>isPdfFile,noContextMenu:()=>noContextMenu,normalizeUnicode:()=>normalizeUnicode,setLayerDimensions:()=>setLayerDimensions,shadow:()=>shadow,version:()=>Kt});const e=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),i=[1,0,0,1,0,0],s=[.001,0,0,.001,0,0],n=1.35,a=1,r=2,o=4,l=16,h=32,d=64,c=128,u=256,p={DISABLE:0,ENABLE:1,ENABLE_FORMS:2,ENABLE_STORAGE:3},g={DISABLE:-1,NONE:0,FREETEXT:3,HIGHLIGHT:9,STAMP:13,INK:15},m={RESIZE:1,CREATE:2,FREETEXT_SIZE:11,FREETEXT_COLOR:12,FREETEXT_OPACITY:13,INK_COLOR:21,INK_THICKNESS:22,INK_OPACITY:23,HIGHLIGHT_COLOR:31,HIGHLIGHT_DEFAULT_COLOR:32,HIGHLIGHT_THICKNESS:33,HIGHLIGHT_FREE:34,HIGHLIGHT_SHOW_ALL:35},f={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},b=0,A=1,v=2,y=3,w=3,x=4,_={GRAYSCALE_1BPP:1,RGB_24BPP:2,RGBA_32BPP:3},E=1,C=2,S=3,T=4,M=5,k=6,P=7,F=8,D=9,R=10,I=11,L=12,N=13,O=14,B=15,H=16,z=17,U=20,j=1,W=2,G=3,$=4,V=5,q={ERRORS:0,WARNINGS:1,INFOS:5},X={NONE:0,BINARY:1},K={dependency:1,setLineWidth:2,setLineCap:3,setLineJoin:4,setMiterLimit:5,setDash:6,setRenderingIntent:7,setFlatness:8,setGState:9,save:10,restore:11,transform:12,moveTo:13,lineTo:14,curveTo:15,curveTo2:16,curveTo3:17,closePath:18,rectangle:19,stroke:20,closeStroke:21,fill:22,eoFill:23,fillStroke:24,eoFillStroke:25,closeFillStroke:26,closeEOFillStroke:27,endPath:28,clip:29,eoClip:30,beginText:31,endText:32,setCharSpacing:33,setWordSpacing:34,setHScale:35,setLeading:36,setFont:37,setTextRenderingMode:38,setTextRise:39,moveText:40,setLeadingMoveText:41,setTextMatrix:42,nextLine:43,showText:44,showSpacedText:45,nextLineShowText:46,nextLineSetSpacingShowText:47,setCharWidth:48,setCharWidthAndBounds:49,setStrokeColorSpace:50,setFillColorSpace:51,setStrokeColor:52,setStrokeColorN:53,setFillColor:54,setFillColorN:55,setStrokeGray:56,setFillGray:57,setStrokeRGBColor:58,setFillRGBColor:59,setStrokeCMYKColor:60,setFillCMYKColor:61,shadingFill:62,beginInlineImage:63,beginImageData:64,endInlineImage:65,paintXObject:66,markPoint:67,markPointProps:68,beginMarkedContent:69,beginMarkedContentProps:70,endMarkedContent:71,beginCompat:72,endCompat:73,paintFormXObjectBegin:74,paintFormXObjectEnd:75,beginGroup:76,endGroup:77,beginAnnotation:80,endAnnotation:81,paintImageMaskXObject:83,paintImageMaskXObjectGroup:84,paintImageXObject:85,paintInlineImageXObject:86,paintInlineImageXObjectGroup:87,paintImageXObjectRepeat:88,paintImageMaskXObjectRepeat:89,paintSolidColorImageMask:90,constructPath:91,setStrokeTransparent:92,setFillTransparent:93},Y={NEED_PASSWORD:1,INCORRECT_PASSWORD:2};let Q=q.WARNINGS;function setVerbosityLevel(t){Number.isInteger(t)&&(Q=t)}function getVerbosityLevel(){return Q}function info(t){Q>=q.INFOS&&console.log(`Info: ${t}`)}function warn(t){Q>=q.WARNINGS&&console.log(`Warning: ${t}`)}function unreachable(t){throw new Error(t)}function assert(t,e){t||unreachable(e)}function createValidAbsoluteUrl(t,e=null,i=null){if(!t)return null;try{if(i&&"string"==typeof t){if(i.addDefaultProtocol&&t.startsWith("www.")){const e=t.match(/\./g);e?.length>=2&&(t=`http://${t}`)}if(i.tryConvertEncoding)try{t=function stringToUTF8String(t){return decodeURIComponent(escape(t))}(t)}catch{}}const s=e?new URL(t,e):new URL(t);if(function _isValidProtocol(t){switch(t?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(s))return s}catch{}return null}function shadow(t,e,i,s=!1){Object.defineProperty(t,e,{value:i,enumerable:!s,configurable:!0,writable:!1});return i}const J=function BaseExceptionClosure(){function BaseException(t,e){this.message=t;this.name=e}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends J{constructor(t,e){super(t,"PasswordException");this.code=e}}class UnknownErrorException extends J{constructor(t,e){super(t,"UnknownErrorException");this.details=e}}class InvalidPDFException extends J{constructor(t){super(t,"InvalidPDFException")}}class MissingPDFException extends J{constructor(t){super(t,"MissingPDFException")}}class UnexpectedResponseException extends J{constructor(t,e){super(t,"UnexpectedResponseException");this.status=e}}class FormatError extends J{constructor(t){super(t,"FormatError")}}class AbortException extends J{constructor(t){super(t,"AbortException")}}function bytesToString(t){"object"==typeof t&&void 0!==t?.length||unreachable("Invalid argument for bytesToString");const e=t.length,i=8192;if(e<i)return String.fromCharCode.apply(null,t);const s=[];for(let n=0;n<e;n+=i){const a=Math.min(n+i,e),r=t.subarray(n,a);s.push(String.fromCharCode.apply(null,r))}return s.join("")}function stringToBytes(t){"string"!=typeof t&&unreachable("Invalid argument for stringToBytes");const e=t.length,i=new Uint8Array(e);for(let s=0;s<e;++s)i[s]=255&t.charCodeAt(s);return i}function objectFromMap(t){const e=Object.create(null);for(const[i,s]of t)e[i]=s;return e}class util_FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const t=new Uint8Array(4);t[0]=1;return 1===new Uint32Array(t.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get platform(){return"undefined"!=typeof navigator&&"string"==typeof navigator?.platform?shadow(this,"platform",{isMac:navigator.platform.includes("Mac"),isWindows:navigator.platform.includes("Win"),isFirefox:"string"==typeof navigator?.userAgent&&navigator.userAgent.includes("Firefox")}):shadow(this,"platform",{isMac:!1,isWindows:!1,isFirefox:!1})}static get isCSSRoundSupported(){return shadow(this,"isCSSRoundSupported",globalThis.CSS?.supports?.("width: round(1.5px, 1px)"))}}const Z=Array.from(Array(256).keys(),(t=>t.toString(16).padStart(2,"0")));class Util{static makeHexColor(t,e,i){return`#${Z[t]}${Z[e]}${Z[i]}`}static scaleMinMax(t,e){let i;if(t[0]){if(t[0]<0){i=e[0];e[0]=e[2];e[2]=i}e[0]*=t[0];e[2]*=t[0];if(t[3]<0){i=e[1];e[1]=e[3];e[3]=i}e[1]*=t[3];e[3]*=t[3]}else{i=e[0];e[0]=e[1];e[1]=i;i=e[2];e[2]=e[3];e[3]=i;if(t[1]<0){i=e[1];e[1]=e[3];e[3]=i}e[1]*=t[1];e[3]*=t[1];if(t[2]<0){i=e[0];e[0]=e[2];e[2]=i}e[0]*=t[2];e[2]*=t[2]}e[0]+=t[4];e[1]+=t[5];e[2]+=t[4];e[3]+=t[5]}static transform(t,e){return[t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]]}static applyTransform(t,e){return[t[0]*e[0]+t[1]*e[2]+e[4],t[0]*e[1]+t[1]*e[3]+e[5]]}static applyInverseTransform(t,e){const i=e[0]*e[3]-e[1]*e[2];return[(t[0]*e[3]-t[1]*e[2]+e[2]*e[5]-e[4]*e[3])/i,(-t[0]*e[1]+t[1]*e[0]+e[4]*e[1]-e[5]*e[0])/i]}static getAxialAlignedBoundingBox(t,e){const i=this.applyTransform(t,e),s=this.applyTransform(t.slice(2,4),e),n=this.applyTransform([t[0],t[3]],e),a=this.applyTransform([t[2],t[1]],e);return[Math.min(i[0],s[0],n[0],a[0]),Math.min(i[1],s[1],n[1],a[1]),Math.max(i[0],s[0],n[0],a[0]),Math.max(i[1],s[1],n[1],a[1])]}static inverseTransform(t){const e=t[0]*t[3]-t[1]*t[2];return[t[3]/e,-t[1]/e,-t[2]/e,t[0]/e,(t[2]*t[5]-t[4]*t[3])/e,(t[4]*t[1]-t[5]*t[0])/e]}static singularValueDecompose2dScale(t){const e=[t[0],t[2],t[1],t[3]],i=t[0]*e[0]+t[1]*e[2],s=t[0]*e[1]+t[1]*e[3],n=t[2]*e[0]+t[3]*e[2],a=t[2]*e[1]+t[3]*e[3],r=(i+a)/2,o=Math.sqrt((i+a)**2-4*(i*a-n*s))/2,l=r+o||1,h=r-o||1;return[Math.sqrt(l),Math.sqrt(h)]}static normalizeRect(t){const e=t.slice(0);if(t[0]>t[2]){e[0]=t[2];e[2]=t[0]}if(t[1]>t[3]){e[1]=t[3];e[3]=t[1]}return e}static intersect(t,e){const i=Math.max(Math.min(t[0],t[2]),Math.min(e[0],e[2])),s=Math.min(Math.max(t[0],t[2]),Math.max(e[0],e[2]));if(i>s)return null;const n=Math.max(Math.min(t[1],t[3]),Math.min(e[1],e[3])),a=Math.min(Math.max(t[1],t[3]),Math.max(e[1],e[3]));return n>a?null:[i,n,s,a]}static#t(t,e,i,s,n,a,r,o,l,h){if(l<=0||l>=1)return;const d=1-l,c=l*l,u=c*l,p=d*(d*(d*t+3*l*e)+3*c*i)+u*s,g=d*(d*(d*n+3*l*a)+3*c*r)+u*o;h[0]=Math.min(h[0],p);h[1]=Math.min(h[1],g);h[2]=Math.max(h[2],p);h[3]=Math.max(h[3],g)}static#e(t,e,i,s,n,a,r,o,l,h,d,c){if(Math.abs(l)<1e-12){Math.abs(h)>=1e-12&&this.#t(t,e,i,s,n,a,r,o,-d/h,c);return}const u=h**2-4*d*l;if(u<0)return;const p=Math.sqrt(u),g=2*l;this.#t(t,e,i,s,n,a,r,o,(-h+p)/g,c);this.#t(t,e,i,s,n,a,r,o,(-h-p)/g,c)}static bezierBoundingBox(t,e,i,s,n,a,r,o,l){if(l){l[0]=Math.min(l[0],t,r);l[1]=Math.min(l[1],e,o);l[2]=Math.max(l[2],t,r);l[3]=Math.max(l[3],e,o)}else l=[Math.min(t,r),Math.min(e,o),Math.max(t,r),Math.max(e,o)];this.#e(t,i,n,r,e,s,a,o,3*(3*(i-n)-t+r),6*(t-2*i+n),3*(i-t),l);this.#e(t,i,n,r,e,s,a,o,3*(3*(s-a)-e+o),6*(e-2*s+a),3*(s-e),l);return l}}let tt=null,et=null;function normalizeUnicode(t){if(!tt){tt=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;et=new Map([["ſt","ſt"]])}return t.replaceAll(tt,((t,e,i)=>e?e.normalize("NFKC"):et.get(i)))}const it="pdfjs_internal_id_",st=0,nt=1,at=2,rt=3,ot=4,lt=5,ht=6,dt=7,ct=8;class BaseFilterFactory{addFilter(t){return"none"}addHCMFilter(t,e){return"none"}addAlphaFilter(t){return"none"}addLuminosityFilter(t){return"none"}addHighlightHCMFilter(t,e,i,s,n){return"none"}destroy(t=!1){}}class BaseCanvasFactory{#i=!1;constructor({enableHWA:t=!1}){this.#i=t}create(t,e){if(t<=0||e<=0)throw new Error("Invalid canvas size");const i=this._createCanvas(t,e);return{canvas:i,context:i.getContext("2d",{willReadFrequently:!this.#i})}}reset(t,e,i){if(!t.canvas)throw new Error("Canvas is not specified");if(e<=0||i<=0)throw new Error("Invalid canvas size");t.canvas.width=e;t.canvas.height=i}destroy(t){if(!t.canvas)throw new Error("Canvas is not specified");t.canvas.width=0;t.canvas.height=0;t.canvas=null;t.context=null}_createCanvas(t,e){unreachable("Abstract method `_createCanvas` called.")}}class BaseCMapReaderFactory{constructor({baseUrl:t=null,isCompressed:e=!0}){this.baseUrl=t;this.isCompressed=e}async fetch({name:t}){if(!this.baseUrl)throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided.");if(!t)throw new Error("CMap name must be specified.");const e=this.baseUrl+t+(this.isCompressed?".bcmap":""),i=this.isCompressed?X.BINARY:X.NONE;return this._fetchData(e,i).catch((t=>{throw new Error(`Unable to load ${this.isCompressed?"binary ":""}CMap at: ${e}`)}))}_fetchData(t,e){unreachable("Abstract method `_fetchData` called.")}}class BaseStandardFontDataFactory{constructor({baseUrl:t=null}){this.baseUrl=t}async fetch({filename:t}){if(!this.baseUrl)throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided.");if(!t)throw new Error("Font filename must be specified.");const e=`${this.baseUrl}${t}`;return this._fetchData(e).catch((t=>{throw new Error(`Unable to load font data at: ${e}`)}))}_fetchData(t){unreachable("Abstract method `_fetchData` called.")}}class BaseSVGFactory{create(t,e,i=!1){if(t<=0||e<=0)throw new Error("Invalid SVG dimensions");const s=this._createSVG("svg:svg");s.setAttribute("version","1.1");if(!i){s.setAttribute("width",`${t}px`);s.setAttribute("height",`${e}px`)}s.setAttribute("preserveAspectRatio","none");s.setAttribute("viewBox",`0 0 ${t} ${e}`);return s}createElement(t){if("string"!=typeof t)throw new Error("Invalid SVG element type");return this._createSVG(t)}_createSVG(t){unreachable("Abstract method `_createSVG` called.")}}const ut="http://www.w3.org/2000/svg";class PixelsPerInch{static CSS=96;static PDF=72;static PDF_TO_CSS_UNITS=this.CSS/this.PDF}async function fetchData(t,e="text"){if(isValidFetchUrl(t,document.baseURI)){const i=await fetch(t);if(!i.ok)throw new Error(i.statusText);switch(e){case"arraybuffer":return i.arrayBuffer();case"blob":return i.blob();case"json":return i.json()}return i.text()}return new Promise(((i,s)=>{const n=new XMLHttpRequest;n.open("GET",t,!0);n.responseType=e;n.onreadystatechange=()=>{if(n.readyState===XMLHttpRequest.DONE)if(200!==n.status&&0!==n.status)s(new Error(n.statusText));else{switch(e){case"arraybuffer":case"blob":case"json":i(n.response);return}i(n.responseText)}};n.send(null)}))}class DOMCMapReaderFactory extends BaseCMapReaderFactory{_fetchData(t,e){return fetchData(t,this.isCompressed?"arraybuffer":"text").then((t=>({cMapData:t instanceof ArrayBuffer?new Uint8Array(t):stringToBytes(t),compressionType:e})))}}class DOMStandardFontDataFactory extends BaseStandardFontDataFactory{_fetchData(t){return fetchData(t,"arraybuffer").then((t=>new Uint8Array(t)))}}class DOMSVGFactory extends BaseSVGFactory{_createSVG(t){return document.createElementNS(ut,t)}}class PageViewport{constructor({viewBox:t,scale:e,rotation:i,offsetX:s=0,offsetY:n=0,dontFlip:a=!1}){this.viewBox=t;this.scale=e;this.rotation=i;this.offsetX=s;this.offsetY=n;const r=(t[2]+t[0])/2,o=(t[3]+t[1])/2;let l,h,d,c,u,p,g,m;(i%=360)<0&&(i+=360);switch(i){case 180:l=-1;h=0;d=0;c=1;break;case 90:l=0;h=1;d=1;c=0;break;case 270:l=0;h=-1;d=-1;c=0;break;case 0:l=1;h=0;d=0;c=-1;break;default:throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees.")}if(a){d=-d;c=-c}if(0===l){u=Math.abs(o-t[1])*e+s;p=Math.abs(r-t[0])*e+n;g=(t[3]-t[1])*e;m=(t[2]-t[0])*e}else{u=Math.abs(r-t[0])*e+s;p=Math.abs(o-t[1])*e+n;g=(t[2]-t[0])*e;m=(t[3]-t[1])*e}this.transform=[l*e,h*e,d*e,c*e,u-l*e*r-d*e*o,p-h*e*r-c*e*o];this.width=g;this.height=m}get rawDims(){const{viewBox:t}=this;return shadow(this,"rawDims",{pageWidth:t[2]-t[0],pageHeight:t[3]-t[1],pageX:t[0],pageY:t[1]})}clone({scale:t=this.scale,rotation:e=this.rotation,offsetX:i=this.offsetX,offsetY:s=this.offsetY,dontFlip:n=!1}={}){return new PageViewport({viewBox:this.viewBox.slice(),scale:t,rotation:e,offsetX:i,offsetY:s,dontFlip:n})}convertToViewportPoint(t,e){return Util.applyTransform([t,e],this.transform)}convertToViewportRectangle(t){const e=Util.applyTransform([t[0],t[1]],this.transform),i=Util.applyTransform([t[2],t[3]],this.transform);return[e[0],e[1],i[0],i[1]]}convertToPdfPoint(t,e){return Util.applyInverseTransform([t,e],this.transform)}}class RenderingCancelledException extends J{constructor(t,e=0){super(t,"RenderingCancelledException");this.extraDelay=e}}function isDataScheme(t){const e=t.length;let i=0;for(;i<e&&""===t[i].trim();)i++;return"data:"===t.substring(i,i+5).toLowerCase()}function isPdfFile(t){return"string"==typeof t&&/\.pdf$/i.test(t)}function getFilenameFromUrl(t){[t]=t.split(/[#?]/,1);return t.substring(t.lastIndexOf("/")+1)}function getPdfFilenameFromUrl(t,e="document.pdf"){if("string"!=typeof t)return e;if(isDataScheme(t)){warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.');return e}const i=/[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i,s=/^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/.exec(t);let n=i.exec(s[1])||i.exec(s[2])||i.exec(s[3]);if(n){n=n[0];if(n.includes("%"))try{n=i.exec(decodeURIComponent(n))[0]}catch{}}return n||e}class StatTimer{started=Object.create(null);times=[];time(t){t in this.started&&warn(`Timer is already running for ${t}`);this.started[t]=Date.now()}timeEnd(t){t in this.started||warn(`Timer has not been started for ${t}`);this.times.push({name:t,start:this.started[t],end:Date.now()});delete this.started[t]}toString(){const t=[];let e=0;for(const{name:t}of this.times)e=Math.max(t.length,e);for(const{name:i,start:s,end:n}of this.times)t.push(`${i.padEnd(e)} ${n-s}ms\n`);return t.join("")}}function isValidFetchUrl(t,e){try{const{protocol:i}=e?new URL(t,e):new URL(t);return"http:"===i||"https:"===i}catch{return!1}}function noContextMenu(t){t.preventDefault()}function deprecated(t){console.log("Deprecated API usage: "+t)}let pt;class PDFDateString{static toDateObject(t){if(!t||"string"!=typeof t)return null;pt||=new RegExp("^D:(\\d{4})(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?([Z|+|-])?(\\d{2})?'?(\\d{2})?'?");const e=pt.exec(t);if(!e)return null;const i=parseInt(e[1],10);let s=parseInt(e[2],10);s=s>=1&&s<=12?s-1:0;let n=parseInt(e[3],10);n=n>=1&&n<=31?n:1;let a=parseInt(e[4],10);a=a>=0&&a<=23?a:0;let r=parseInt(e[5],10);r=r>=0&&r<=59?r:0;let o=parseInt(e[6],10);o=o>=0&&o<=59?o:0;const l=e[7]||"Z";let h=parseInt(e[8],10);h=h>=0&&h<=23?h:0;let d=parseInt(e[9],10)||0;d=d>=0&&d<=59?d:0;if("-"===l){a+=h;r+=d}else if("+"===l){a-=h;r-=d}return new Date(Date.UTC(i,s,n,a,r,o))}}function getXfaPageViewport(t,{scale:e=1,rotation:i=0}){const{width:s,height:n}=t.attributes.style,a=[0,0,parseInt(s),parseInt(n)];return new PageViewport({viewBox:a,scale:e,rotation:i})}function getRGB(t){if(t.startsWith("#")){const e=parseInt(t.slice(1),16);return[(16711680&e)>>16,(65280&e)>>8,255&e]}if(t.startsWith("rgb("))return t.slice(4,-1).split(",").map((t=>parseInt(t)));if(t.startsWith("rgba("))return t.slice(5,-1).split(",").map((t=>parseInt(t))).slice(0,3);warn(`Not a valid color format: "${t}"`);return[0,0,0]}function getCurrentTransform(t){const{a:e,b:i,c:s,d:n,e:a,f:r}=t.getTransform();return[e,i,s,n,a,r]}function getCurrentTransformInverse(t){const{a:e,b:i,c:s,d:n,e:a,f:r}=t.getTransform().invertSelf();return[e,i,s,n,a,r]}function setLayerDimensions(t,e,i=!1,s=!0){if(e instanceof PageViewport){const{pageWidth:s,pageHeight:n}=e.rawDims,{style:a}=t,r=util_FeatureTest.isCSSRoundSupported,o=`var(--scale-factor) * ${s}px`,l=`var(--scale-factor) * ${n}px`,h=r?`round(down, ${o}, var(--scale-round-x, 1px))`:`calc(${o})`,d=r?`round(down, ${l}, var(--scale-round-y, 1px))`:`calc(${l})`;if(i&&e.rotation%180!=0){a.width=d;a.height=h}else{a.width=h;a.height=d}}s&&t.setAttribute("data-main-rotation",e.rotation)}class OutputScale{constructor(){const t=window.devicePixelRatio||1;this.sx=t;this.sy=t}get scaled(){return 1!==this.sx||1!==this.sy}get symmetric(){return this.sx===this.sy}}class EditorToolbar{#s=null;#n=null;#a;#r=null;#o=null;static#l=null;constructor(t){this.#a=t;EditorToolbar.#l||=Object.freeze({freetext:"pdfjs-editor-remove-freetext-button",highlight:"pdfjs-editor-remove-highlight-button",ink:"pdfjs-editor-remove-ink-button",stamp:"pdfjs-editor-remove-stamp-button"})}render(){const t=this.#s=document.createElement("div");t.classList.add("editToolbar","hidden");t.setAttribute("role","toolbar");const e=this.#a._uiManager._signal;t.addEventListener("contextmenu",noContextMenu,{signal:e});t.addEventListener("pointerdown",EditorToolbar.#h,{signal:e});const i=this.#r=document.createElement("div");i.className="buttons";t.append(i);const s=this.#a.toolbarPosition;if(s){const{style:e}=t,i="ltr"===this.#a._uiManager.direction?1-s[0]:s[0];e.insetInlineEnd=100*i+"%";e.top=`calc(${100*s[1]}% + var(--editor-toolbar-vert-offset))`}this.#d();return t}get div(){return this.#s}static#h(t){t.stopPropagation()}#c(t){this.#a._focusEventsAllowed=!1;t.preventDefault();t.stopPropagation()}#u(t){this.#a._focusEventsAllowed=!0;t.preventDefault();t.stopPropagation()}#p(t){const e=this.#a._uiManager._signal;t.addEventListener("focusin",this.#c.bind(this),{capture:!0,signal:e});t.addEventListener("focusout",this.#u.bind(this),{capture:!0,signal:e});t.addEventListener("contextmenu",noContextMenu,{signal:e})}hide(){this.#s.classList.add("hidden");this.#n?.hideDropdown()}show(){this.#s.classList.remove("hidden");this.#o?.shown()}#d(){const{editorType:t,_uiManager:e}=this.#a,i=document.createElement("button");i.className="delete";i.tabIndex=0;i.setAttribute("data-l10n-id",EditorToolbar.#l[t]);this.#p(i);i.addEventListener("click",(t=>{e.delete()}),{signal:e._signal});this.#r.append(i)}get#g(){const t=document.createElement("div");t.className="divider";return t}async addAltText(t){const e=await t.render();this.#p(e);this.#r.prepend(e,this.#g);this.#o=t}addColorPicker(t){this.#n=t;const e=t.renderButton();this.#p(e);this.#r.prepend(e,this.#g)}remove(){this.#s.remove();this.#n?.destroy();this.#n=null}}class HighlightToolbar{#r=null;#s=null;#m;constructor(t){this.#m=t}#f(){const t=this.#s=document.createElement("div");t.className="editToolbar";t.setAttribute("role","toolbar");t.addEventListener("contextmenu",noContextMenu,{signal:this.#m._signal});const e=this.#r=document.createElement("div");e.className="buttons";t.append(e);this.#b();return t}#A(t,e){let i=0,s=0;for(const n of t){const t=n.y+n.height;if(t<i)continue;const a=n.x+(e?n.width:0);if(t>i){s=a;i=t}else e?a>s&&(s=a):a<s&&(s=a)}return[e?1-s:s,i]}show(t,e,i){const[s,n]=this.#A(e,i),{style:a}=this.#s||=this.#f();t.append(this.#s);a.insetInlineEnd=100*s+"%";a.top=`calc(${100*n}% + var(--editor-toolbar-vert-offset))`}hide(){this.#s.remove()}#b(){const t=document.createElement("button");t.className="highlightButton";t.tabIndex=0;t.setAttribute("data-l10n-id","pdfjs-highlight-floating-button1");const e=document.createElement("span");t.append(e);e.className="visuallyHidden";e.setAttribute("data-l10n-id","pdfjs-highlight-floating-button-label");const i=this.#m._signal;t.addEventListener("contextmenu",noContextMenu,{signal:i});t.addEventListener("click",(()=>{this.#m.highlightSelection("floating_button")}),{signal:i});this.#r.append(t)}}function bindEvents(t,e,i){for(const s of i)e.addEventListener(s,t[s].bind(t))}class IdManager{#v=0;get id(){return"pdfjs_internal_editor_"+this.#v++}}class ImageManager{#y=function getUuid(){if("undefined"!=typeof crypto&&"function"==typeof crypto?.randomUUID)return crypto.randomUUID();const t=new Uint8Array(32);if("undefined"!=typeof crypto&&"function"==typeof crypto?.getRandomValues)crypto.getRandomValues(t);else for(let e=0;e<32;e++)t[e]=Math.floor(255*Math.random());return bytesToString(t)}();#v=0;#w=null;static get _isSVGFittingCanvas(){const t=new OffscreenCanvas(1,3).getContext("2d",{willReadFrequently:!0}),e=new Image;e.src='data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 1 1" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><rect width="1" height="1" style="fill:red;"/></svg>';return shadow(this,"_isSVGFittingCanvas",e.decode().then((()=>{t.drawImage(e,0,0,1,1,0,0,1,3);return 0===new Uint32Array(t.getImageData(0,0,1,1).data.buffer)[0]})))}async#x(t,e){this.#w||=new Map;let i=this.#w.get(t);if(null===i)return null;if(i?.bitmap){i.refCounter+=1;return i}try{i||={bitmap:null,id:`image_${this.#y}_${this.#v++}`,refCounter:0,isSvg:!1};let t;if("string"==typeof e){i.url=e;t=await fetchData(e,"blob")}else e instanceof File?t=i.file=e:e instanceof Blob&&(t=e);if("image/svg+xml"===t.type){const e=ImageManager._isSVGFittingCanvas,s=new FileReader,n=new Image,a=new Promise(((t,a)=>{n.onload=()=>{i.bitmap=n;i.isSvg=!0;t()};s.onload=async()=>{const t=i.svgUrl=s.result;n.src=await e?`${t}#svgView(preserveAspectRatio(none))`:t};n.onerror=s.onerror=a}));s.readAsDataURL(t);await a}else i.bitmap=await createImageBitmap(t);i.refCounter=1}catch(t){console.error(t);i=null}this.#w.set(t,i);i&&this.#w.set(i.id,i);return i}async getFromFile(t){const{lastModified:e,name:i,size:s,type:n}=t;return this.#x(`${e}_${i}_${s}_${n}`,t)}async getFromUrl(t){return this.#x(t,t)}async getFromBlob(t,e){const i=await e;return this.#x(t,i)}async getFromId(t){this.#w||=new Map;const e=this.#w.get(t);if(!e)return null;if(e.bitmap){e.refCounter+=1;return e}if(e.file)return this.getFromFile(e.file);if(e.blobPromise){const{blobPromise:t}=e;delete e.blobPromise;return this.getFromBlob(e.id,t)}return this.getFromUrl(e.url)}getFromCanvas(t,e){this.#w||=new Map;let i=this.#w.get(t);if(i?.bitmap){i.refCounter+=1;return i}const s=new OffscreenCanvas(e.width,e.height);s.getContext("2d").drawImage(e,0,0);i={bitmap:s.transferToImageBitmap(),id:`image_${this.#y}_${this.#v++}`,refCounter:1,isSvg:!1};this.#w.set(t,i);this.#w.set(i.id,i);return i}getSvgUrl(t){const e=this.#w.get(t);return e?.isSvg?e.svgUrl:null}deleteId(t){this.#w||=new Map;const e=this.#w.get(t);if(!e)return;e.refCounter-=1;if(0!==e.refCounter)return;const{bitmap:i}=e;if(!e.url&&!e.file){const t=new OffscreenCanvas(i.width,i.height);t.getContext("bitmaprenderer").transferFromImageBitmap(i);e.blobPromise=t.convertToBlob()}i.close?.();e.bitmap=null}isValidId(t){return t.startsWith(`image_${this.#y}_`)}}class CommandManager{#_=[];#E=!1;#C;#S=-1;constructor(t=128){this.#C=t}add({cmd:t,undo:e,post:i,mustExec:s,type:n=NaN,overwriteIfSameType:a=!1,keepUndo:r=!1}){s&&t();if(this.#E)return;const o={cmd:t,undo:e,post:i,type:n};if(-1===this.#S){this.#_.length>0&&(this.#_.length=0);this.#S=0;this.#_.push(o);return}if(a&&this.#_[this.#S].type===n){r&&(o.undo=this.#_[this.#S].undo);this.#_[this.#S]=o;return}const l=this.#S+1;if(l===this.#C)this.#_.splice(0,1);else{this.#S=l;l<this.#_.length&&this.#_.splice(l)}this.#_.push(o)}undo(){if(-1===this.#S)return;this.#E=!0;const{undo:t,post:e}=this.#_[this.#S];t();e?.();this.#E=!1;this.#S-=1}redo(){if(this.#S<this.#_.length-1){this.#S+=1;this.#E=!0;const{cmd:t,post:e}=this.#_[this.#S];t();e?.();this.#E=!1}}hasSomethingToUndo(){return-1!==this.#S}hasSomethingToRedo(){return this.#S<this.#_.length-1}destroy(){this.#_=null}}class KeyboardManager{constructor(t){this.buffer=[];this.callbacks=new Map;this.allKeys=new Set;const{isMac:e}=util_FeatureTest.platform;for(const[i,s,n={}]of t)for(const t of i){const i=t.startsWith("mac+");if(e&&i){this.callbacks.set(t.slice(4),{callback:s,options:n});this.allKeys.add(t.split("+").at(-1))}else if(!e&&!i){this.callbacks.set(t,{callback:s,options:n});this.allKeys.add(t.split("+").at(-1))}}}#T(t){t.altKey&&this.buffer.push("alt");t.ctrlKey&&this.buffer.push("ctrl");t.metaKey&&this.buffer.push("meta");t.shiftKey&&this.buffer.push("shift");this.buffer.push(t.key);const e=this.buffer.join("+");this.buffer.length=0;return e}exec(t,e){if(!this.allKeys.has(e.key))return;const i=this.callbacks.get(this.#T(e));if(!i)return;const{callback:s,options:{bubbles:n=!1,args:a=[],checker:r=null}}=i;if(!r||r(t,e)){s.bind(t,...a,e)();if(!n){e.stopPropagation();e.preventDefault()}}}}class ColorManager{static _colorsMapping=new Map([["CanvasText",[0,0,0]],["Canvas",[255,255,255]]]);get _colors(){const t=new Map([["CanvasText",null],["Canvas",null]]);!function getColorValues(t){const e=document.createElement("span");e.style.visibility="hidden";document.body.append(e);for(const i of t.keys()){e.style.color=i;const s=window.getComputedStyle(e).color;t.set(i,getRGB(s))}e.remove()}(t);return shadow(this,"_colors",t)}convert(t){const e=getRGB(t);if(!window.matchMedia("(forced-colors: active)").matches)return e;for(const[t,i]of this._colors)if(i.every(((t,i)=>t===e[i])))return ColorManager._colorsMapping.get(t);return e}getHexCode(t){const e=this._colors.get(t);return e?Util.makeHexColor(...e):t}}class AnnotationEditorUIManager{#M=new AbortController;#k=null;#P=new Map;#F=new Map;#D=null;#R=null;#I=null;#L=new CommandManager;#N=null;#O=0;#B=new Set;#H=null;#z=null;#U=new Set;#j=!1;#W=!1;#G=!1;#$=null;#V=null;#q=null;#X=null;#K=!1;#Y=null;#Q=new IdManager;#J=!1;#Z=!1;#tt=null;#et=null;#it=null;#st=null;#nt=g.NONE;#at=new Set;#rt=null;#ot=null;#lt=null;#ht={isEditing:!1,isEmpty:!0,hasSomethingToUndo:!1,hasSomethingToRedo:!1,hasSelectedEditor:!1,hasSelectedText:!1};#dt=[0,0];#ct=null;#ut=null;#pt=null;#gt=null;static TRANSLATE_SMALL=1;static TRANSLATE_BIG=10;static get _keyboardManager(){const t=AnnotationEditorUIManager.prototype,arrowChecker=t=>t.#ut.contains(document.activeElement)&&"BUTTON"!==document.activeElement.tagName&&t.hasSomethingToControl(),textInputChecker=(t,{target:e})=>{if(e instanceof HTMLInputElement){const{type:t}=e;return"text"!==t&&"number"!==t}return!0},e=this.TRANSLATE_SMALL,i=this.TRANSLATE_BIG;return shadow(this,"_keyboardManager",new KeyboardManager([[["ctrl+a","mac+meta+a"],t.selectAll,{checker:textInputChecker}],[["ctrl+z","mac+meta+z"],t.undo,{checker:textInputChecker}],[["ctrl+y","ctrl+shift+z","mac+meta+shift+z","ctrl+shift+Z","mac+meta+shift+Z"],t.redo,{checker:textInputChecker}],[["Backspace","alt+Backspace","ctrl+Backspace","shift+Backspace","mac+Backspace","mac+alt+Backspace","mac+ctrl+Backspace","Delete","ctrl+Delete","shift+Delete","mac+Delete"],t.delete,{checker:textInputChecker}],[["Enter","mac+Enter"],t.addNewEditorFromKeyboard,{checker:(t,{target:e})=>!(e instanceof HTMLButtonElement)&&t.#ut.contains(e)&&!t.isEnterHandled}],[[" ","mac+ "],t.addNewEditorFromKeyboard,{checker:(t,{target:e})=>!(e instanceof HTMLButtonElement)&&t.#ut.contains(document.activeElement)}],[["Escape","mac+Escape"],t.unselectAll],[["ArrowLeft","mac+ArrowLeft"],t.translateSelectedEditors,{args:[-e,0],checker:arrowChecker}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t.translateSelectedEditors,{args:[-i,0],checker:arrowChecker}],[["ArrowRight","mac+ArrowRight"],t.translateSelectedEditors,{args:[e,0],checker:arrowChecker}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t.translateSelectedEditors,{args:[i,0],checker:arrowChecker}],[["ArrowUp","mac+ArrowUp"],t.translateSelectedEditors,{args:[0,-e],checker:arrowChecker}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t.translateSelectedEditors,{args:[0,-i],checker:arrowChecker}],[["ArrowDown","mac+ArrowDown"],t.translateSelectedEditors,{args:[0,e],checker:arrowChecker}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t.translateSelectedEditors,{args:[0,i],checker:arrowChecker}]]))}constructor(t,e,i,s,n,a,r,o,l,h,d){const c=this._signal=this.#M.signal;this.#ut=t;this.#pt=e;this.#D=i;this._eventBus=s;s._on("editingaction",this.onEditingAction.bind(this),{signal:c});s._on("pagechanging",this.onPageChanging.bind(this),{signal:c});s._on("scalechanging",this.onScaleChanging.bind(this),{signal:c});s._on("rotationchanging",this.onRotationChanging.bind(this),{signal:c});s._on("setpreference",this.onSetPreference.bind(this),{signal:c});s._on("switchannotationeditorparams",(t=>this.updateParams(t.type,t.value)),{signal:c});this.#mt();this.#ft();this.#bt();this.#R=n.annotationStorage;this.#$=n.filterFactory;this.#ot=a;this.#X=r||null;this.#j=o;this.#W=l;this.#G=h;this.#st=d||null;this.viewParameters={realScale:PixelsPerInch.PDF_TO_CSS_UNITS,rotation:0};this.isShiftKeyDown=!1}destroy(){this.#gt?.resolve();this.#gt=null;this.#M?.abort();this.#M=null;this._signal=null;for(const t of this.#F.values())t.destroy();this.#F.clear();this.#P.clear();this.#U.clear();this.#k=null;this.#at.clear();this.#L.destroy();this.#D?.destroy();this.#Y?.hide();this.#Y=null;if(this.#V){clearTimeout(this.#V);this.#V=null}if(this.#ct){clearTimeout(this.#ct);this.#ct=null}}combinedSignal(t){return AbortSignal.any([this._signal,t.signal])}get mlManager(){return this.#st}get useNewAltTextFlow(){return this.#W}get useNewAltTextWhenAddingImage(){return this.#G}get hcmFilter(){return shadow(this,"hcmFilter",this.#ot?this.#$.addHCMFilter(this.#ot.foreground,this.#ot.background):"none")}get direction(){return shadow(this,"direction",getComputedStyle(this.#ut).direction)}get highlightColors(){return shadow(this,"highlightColors",this.#X?new Map(this.#X.split(",").map((t=>t.split("=").map((t=>t.trim()))))):null)}get highlightColorNames(){return shadow(this,"highlightColorNames",this.highlightColors?new Map(Array.from(this.highlightColors,(t=>t.reverse()))):null)}setMainHighlightColorPicker(t){this.#it=t}editAltText(t,e=!1){this.#D?.editAltText(this,t,e)}switchToMode(t,e){this._eventBus.on("annotationeditormodechanged",e,{once:!0,signal:this._signal});this._eventBus.dispatch("showannotationeditorui",{source:this,mode:t})}setPreference(t,e){this._eventBus.dispatch("setpreference",{source:this,name:t,value:e})}onSetPreference({name:t,value:e}){if("enableNewAltTextWhenAddingImage"===t)this.#G=e}onPageChanging({pageNumber:t}){this.#O=t-1}focusMainContainer(){this.#ut.focus()}findParent(t,e){for(const i of this.#F.values()){const{x:s,y:n,width:a,height:r}=i.div.getBoundingClientRect();if(t>=s&&t<=s+a&&e>=n&&e<=n+r)return i}return null}disableUserSelect(t=!1){this.#pt.classList.toggle("noUserSelect",t)}addShouldRescale(t){this.#U.add(t)}removeShouldRescale(t){this.#U.delete(t)}onScaleChanging({scale:t}){this.commitOrRemove();this.viewParameters.realScale=t*PixelsPerInch.PDF_TO_CSS_UNITS;for(const t of this.#U)t.onScaleChanging()}onRotationChanging({pagesRotation:t}){this.commitOrRemove();this.viewParameters.rotation=t}#At({anchorNode:t}){return t.nodeType===Node.TEXT_NODE?t.parentElement:t}#vt(t){const{currentLayer:e}=this;if(e.hasTextLayer(t))return e;for(const e of this.#F.values())if(e.hasTextLayer(t))return e;return null}highlightSelection(t=""){const e=document.getSelection();if(!e||e.isCollapsed)return;const{anchorNode:i,anchorOffset:s,focusNode:n,focusOffset:a}=e,r=e.toString(),o=this.#At(e).closest(".textLayer"),l=this.getSelectionBoxes(o);if(!l)return;e.empty();const h=this.#vt(o),d=this.#nt===g.NONE,callback=()=>{h?.createAndAddNewEditor({x:0,y:0},!1,{methodOfCreation:t,boxes:l,anchorNode:i,anchorOffset:s,focusNode:n,focusOffset:a,text:r});d&&this.showAllEditors("highlight",!0,!0)};d?this.switchToMode(g.HIGHLIGHT,callback):callback()}#yt(){const t=document.getSelection();if(!t||t.isCollapsed)return;const e=this.#At(t).closest(".textLayer"),i=this.getSelectionBoxes(e);if(i){this.#Y||=new HighlightToolbar(this);this.#Y.show(e,i,"ltr"===this.direction)}}addToAnnotationStorage(t){t.isEmpty()||!this.#R||this.#R.has(t.id)||this.#R.setValue(t.id,t)}#wt(){const t=document.getSelection();if(!t||t.isCollapsed){if(this.#rt){this.#Y?.hide();this.#rt=null;this.#xt({hasSelectedText:!1})}return}const{anchorNode:e}=t;if(e===this.#rt)return;const i=this.#At(t).closest(".textLayer");if(i){this.#Y?.hide();this.#rt=e;this.#xt({hasSelectedText:!0});if(this.#nt===g.HIGHLIGHT||this.#nt===g.NONE){this.#nt===g.HIGHLIGHT&&this.showAllEditors("highlight",!0,!0);this.#K=this.isShiftKeyDown;if(!this.isShiftKeyDown){const t=this.#nt===g.HIGHLIGHT?this.#vt(i):null;t?.toggleDrawing();const e=new AbortController,s=this.combinedSignal(e),pointerup=i=>{if("pointerup"!==i.type||0===i.button){e.abort();t?.toggleDrawing(!0);"pointerup"===i.type&&this.#_t("main_toolbar")}};window.addEventListener("pointerup",pointerup,{signal:s});window.addEventListener("blur",pointerup,{signal:s})}}}else if(this.#rt){this.#Y?.hide();this.#rt=null;this.#xt({hasSelectedText:!1})}}#_t(t=""){this.#nt===g.HIGHLIGHT?this.highlightSelection(t):this.#j&&this.#yt()}#mt(){document.addEventListener("selectionchange",this.#wt.bind(this),{signal:this._signal})}#Et(){if(this.#q)return;this.#q=new AbortController;const t=this.combinedSignal(this.#q);window.addEventListener("focus",this.focus.bind(this),{signal:t});window.addEventListener("blur",this.blur.bind(this),{signal:t})}#Ct(){this.#q?.abort();this.#q=null}blur(){this.isShiftKeyDown=!1;if(this.#K){this.#K=!1;this.#_t("main_toolbar")}if(!this.hasSelection)return;const{activeElement:t}=document;for(const e of this.#at)if(e.div.contains(t)){this.#et=[e,t];e._focusEventsAllowed=!1;break}}focus(){if(!this.#et)return;const[t,e]=this.#et;this.#et=null;e.addEventListener("focusin",(()=>{t._focusEventsAllowed=!0}),{once:!0,signal:this._signal});e.focus()}#bt(){if(this.#tt)return;this.#tt=new AbortController;const t=this.combinedSignal(this.#tt);window.addEventListener("keydown",this.keydown.bind(this),{signal:t});window.addEventListener("keyup",this.keyup.bind(this),{signal:t})}#St(){this.#tt?.abort();this.#tt=null}#Tt(){if(this.#N)return;this.#N=new AbortController;const t=this.combinedSignal(this.#N);document.addEventListener("copy",this.copy.bind(this),{signal:t});document.addEventListener("cut",this.cut.bind(this),{signal:t});document.addEventListener("paste",this.paste.bind(this),{signal:t})}#Mt(){this.#N?.abort();this.#N=null}#ft(){const t=this._signal;document.addEventListener("dragover",this.dragOver.bind(this),{signal:t});document.addEventListener("drop",this.drop.bind(this),{signal:t})}addEditListeners(){this.#bt();this.#Tt()}removeEditListeners(){this.#St();this.#Mt()}dragOver(t){for(const{type:e}of t.dataTransfer.items)for(const i of this.#z)if(i.isHandlingMimeForPasting(e)){t.dataTransfer.dropEffect="copy";t.preventDefault();return}}drop(t){for(const e of t.dataTransfer.items)for(const i of this.#z)if(i.isHandlingMimeForPasting(e.type)){i.paste(e,this.currentLayer);t.preventDefault();return}}copy(t){t.preventDefault();this.#k?.commitOrRemove();if(!this.hasSelection)return;const e=[];for(const t of this.#at){const i=t.serialize(!0);i&&e.push(i)}0!==e.length&&t.clipboardData.setData("application/pdfjs",JSON.stringify(e))}cut(t){this.copy(t);this.delete()}async paste(t){t.preventDefault();const{clipboardData:e}=t;for(const t of e.items)for(const e of this.#z)if(e.isHandlingMimeForPasting(t.type)){e.paste(t,this.currentLayer);return}let i=e.getData("application/pdfjs");if(!i)return;try{i=JSON.parse(i)}catch(t){warn(`paste: "${t.message}".`);return}if(!Array.isArray(i))return;this.unselectAll();const s=this.currentLayer;try{const t=[];for(const e of i){const i=await s.deserialize(e);if(!i)return;t.push(i)}const cmd=()=>{for(const e of t)this.#kt(e);this.#Pt(t)},undo=()=>{for(const e of t)e.remove()};this.addCommands({cmd,undo,mustExec:!0})}catch(t){warn(`paste: "${t.message}".`)}}keydown(t){this.isShiftKeyDown||"Shift"!==t.key||(this.isShiftKeyDown=!0);this.#nt===g.NONE||this.isEditorHandlingKeyboard||AnnotationEditorUIManager._keyboardManager.exec(this,t)}keyup(t){if(this.isShiftKeyDown&&"Shift"===t.key){this.isShiftKeyDown=!1;if(this.#K){this.#K=!1;this.#_t("main_toolbar")}}}onEditingAction({name:t}){switch(t){case"undo":case"redo":case"delete":case"selectAll":this[t]();break;case"highlightSelection":this.highlightSelection("context_menu")}}#xt(t){if(Object.entries(t).some((([t,e])=>this.#ht[t]!==e))){this._eventBus.dispatch("annotationeditorstateschanged",{source:this,details:Object.assign(this.#ht,t)});this.#nt===g.HIGHLIGHT&&!1===t.hasSelectedEditor&&this.#Ft([[m.HIGHLIGHT_FREE,!0]])}}#Ft(t){this._eventBus.dispatch("annotationeditorparamschanged",{source:this,details:t})}setEditingState(t){if(t){this.#Et();this.#Tt();this.#xt({isEditing:this.#nt!==g.NONE,isEmpty:this.#Dt(),hasSomethingToUndo:this.#L.hasSomethingToUndo(),hasSomethingToRedo:this.#L.hasSomethingToRedo(),hasSelectedEditor:!1})}else{this.#Ct();this.#Mt();this.#xt({isEditing:!1});this.disableUserSelect(!1)}}registerEditorTypes(t){if(!this.#z){this.#z=t;for(const t of this.#z)this.#Ft(t.defaultPropertiesToUpdate)}}getId(){return this.#Q.id}get currentLayer(){return this.#F.get(this.#O)}getLayer(t){return this.#F.get(t)}get currentPageIndex(){return this.#O}addLayer(t){this.#F.set(t.pageIndex,t);this.#J?t.enable():t.disable()}removeLayer(t){this.#F.delete(t.pageIndex)}async updateMode(t,e=null,i=!1){if(this.#nt!==t){if(this.#gt){await this.#gt.promise;if(!this.#gt)return}this.#gt=Promise.withResolvers();this.#nt=t;if(t!==g.NONE){this.setEditingState(!0);await this.#Rt();this.unselectAll();for(const e of this.#F.values())e.updateMode(t);if(e){for(const t of this.#P.values())if(t.annotationElementId===e){this.setSelected(t);t.enterInEditMode()}else t.unselect();this.#gt.resolve()}else{i&&this.addNewEditorFromKeyboard();this.#gt.resolve()}}else{this.setEditingState(!1);this.#It();this.#gt.resolve()}}}addNewEditorFromKeyboard(){this.currentLayer.canCreateNewEmptyEditor()&&this.currentLayer.addNewEditor()}updateToolbar(t){t!==this.#nt&&this._eventBus.dispatch("switchannotationeditormode",{source:this,mode:t})}updateParams(t,e){if(this.#z){switch(t){case m.CREATE:this.currentLayer.addNewEditor();return;case m.HIGHLIGHT_DEFAULT_COLOR:this.#it?.updateColor(e);break;case m.HIGHLIGHT_SHOW_ALL:this._eventBus.dispatch("reporttelemetry",{source:this,details:{type:"editing",data:{type:"highlight",action:"toggle_visibility"}}});(this.#lt||=new Map).set(t,e);this.showAllEditors("highlight",e)}for(const i of this.#at)i.updateParams(t,e);for(const i of this.#z)i.updateDefaultParams(t,e)}}showAllEditors(t,e,i=!1){for(const i of this.#P.values())i.editorType===t&&i.show(e);(this.#lt?.get(m.HIGHLIGHT_SHOW_ALL)??!0)!==e&&this.#Ft([[m.HIGHLIGHT_SHOW_ALL,e]])}enableWaiting(t=!1){if(this.#Z!==t){this.#Z=t;for(const e of this.#F.values()){t?e.disableClick():e.enableClick();e.div.classList.toggle("waiting",t)}}}async#Rt(){if(!this.#J){this.#J=!0;const t=[];for(const e of this.#F.values())t.push(e.enable());await Promise.all(t);for(const t of this.#P.values())t.enable()}}#It(){this.unselectAll();if(this.#J){this.#J=!1;for(const t of this.#F.values())t.disable();for(const t of this.#P.values())t.disable()}}getEditors(t){const e=[];for(const i of this.#P.values())i.pageIndex===t&&e.push(i);return e}getEditor(t){return this.#P.get(t)}addEditor(t){this.#P.set(t.id,t)}removeEditor(t){if(t.div.contains(document.activeElement)){this.#V&&clearTimeout(this.#V);this.#V=setTimeout((()=>{this.focusMainContainer();this.#V=null}),0)}this.#P.delete(t.id);this.unselect(t);t.annotationElementId&&this.#B.has(t.annotationElementId)||this.#R?.remove(t.id)}addDeletedAnnotationElement(t){this.#B.add(t.annotationElementId);this.addChangedExistingAnnotation(t);t.deleted=!0}isDeletedAnnotationElement(t){return this.#B.has(t)}removeDeletedAnnotationElement(t){this.#B.delete(t.annotationElementId);this.removeChangedExistingAnnotation(t);t.deleted=!1}#kt(t){const e=this.#F.get(t.pageIndex);if(e)e.addOrRebuild(t);else{this.addEditor(t);this.addToAnnotationStorage(t)}}setActiveEditor(t){if(this.#k!==t){this.#k=t;t&&this.#Ft(t.propertiesToUpdate)}}get#Lt(){let t=null;for(t of this.#at);return t}updateUI(t){this.#Lt===t&&this.#Ft(t.propertiesToUpdate)}toggleSelected(t){if(this.#at.has(t)){this.#at.delete(t);t.unselect();this.#xt({hasSelectedEditor:this.hasSelection})}else{this.#at.add(t);t.select();this.#Ft(t.propertiesToUpdate);this.#xt({hasSelectedEditor:!0})}}setSelected(t){for(const e of this.#at)e!==t&&e.unselect();this.#at.clear();this.#at.add(t);t.select();this.#Ft(t.propertiesToUpdate);this.#xt({hasSelectedEditor:!0})}isSelected(t){return this.#at.has(t)}get firstSelectedEditor(){return this.#at.values().next().value}unselect(t){t.unselect();this.#at.delete(t);this.#xt({hasSelectedEditor:this.hasSelection})}get hasSelection(){return 0!==this.#at.size}get isEnterHandled(){return 1===this.#at.size&&this.firstSelectedEditor.isEnterHandled}undo(){this.#L.undo();this.#xt({hasSomethingToUndo:this.#L.hasSomethingToUndo(),hasSomethingToRedo:!0,isEmpty:this.#Dt()})}redo(){this.#L.redo();this.#xt({hasSomethingToUndo:!0,hasSomethingToRedo:this.#L.hasSomethingToRedo(),isEmpty:this.#Dt()})}addCommands(t){this.#L.add(t);this.#xt({hasSomethingToUndo:!0,hasSomethingToRedo:!1,isEmpty:this.#Dt()})}#Dt(){if(0===this.#P.size)return!0;if(1===this.#P.size)for(const t of this.#P.values())return t.isEmpty();return!1}delete(){this.commitOrRemove();if(!this.hasSelection)return;const t=[...this.#at];this.addCommands({cmd:()=>{for(const e of t)e.remove()},undo:()=>{for(const e of t)this.#kt(e)},mustExec:!0})}commitOrRemove(){this.#k?.commitOrRemove()}hasSomethingToControl(){return this.#k||this.hasSelection}#Pt(t){for(const t of this.#at)t.unselect();this.#at.clear();for(const e of t)if(!e.isEmpty()){this.#at.add(e);e.select()}this.#xt({hasSelectedEditor:this.hasSelection})}selectAll(){for(const t of this.#at)t.commit();this.#Pt(this.#P.values())}unselectAll(){if(this.#k){this.#k.commitOrRemove();if(this.#nt!==g.NONE)return}if(this.hasSelection){for(const t of this.#at)t.unselect();this.#at.clear();this.#xt({hasSelectedEditor:!1})}}translateSelectedEditors(t,e,i=!1){i||this.commitOrRemove();if(!this.hasSelection)return;this.#dt[0]+=t;this.#dt[1]+=e;const[s,n]=this.#dt,a=[...this.#at];this.#ct&&clearTimeout(this.#ct);this.#ct=setTimeout((()=>{this.#ct=null;this.#dt[0]=this.#dt[1]=0;this.addCommands({cmd:()=>{for(const t of a)this.#P.has(t.id)&&t.translateInPage(s,n)},undo:()=>{for(const t of a)this.#P.has(t.id)&&t.translateInPage(-s,-n)},mustExec:!1})}),1e3);for(const i of a)i.translateInPage(t,e)}setUpDragSession(){if(this.hasSelection){this.disableUserSelect(!0);this.#H=new Map;for(const t of this.#at)this.#H.set(t,{savedX:t.x,savedY:t.y,savedPageIndex:t.pageIndex,newX:0,newY:0,newPageIndex:-1})}}endDragSession(){if(!this.#H)return!1;this.disableUserSelect(!1);const t=this.#H;this.#H=null;let e=!1;for(const[{x:i,y:s,pageIndex:n},a]of t){a.newX=i;a.newY=s;a.newPageIndex=n;e||=i!==a.savedX||s!==a.savedY||n!==a.savedPageIndex}if(!e)return!1;const move=(t,e,i,s)=>{if(this.#P.has(t.id)){const n=this.#F.get(s);if(n)t._setParentAndPosition(n,e,i);else{t.pageIndex=s;t.x=e;t.y=i}}};this.addCommands({cmd:()=>{for(const[e,{newX:i,newY:s,newPageIndex:n}]of t)move(e,i,s,n)},undo:()=>{for(const[e,{savedX:i,savedY:s,savedPageIndex:n}]of t)move(e,i,s,n)},mustExec:!0});return!0}dragSelectedEditors(t,e){if(this.#H)for(const i of this.#H.keys())i.drag(t,e)}rebuild(t){if(null===t.parent){const e=this.getLayer(t.pageIndex);if(e){e.changeParent(t);e.addOrRebuild(t)}else{this.addEditor(t);this.addToAnnotationStorage(t);t.rebuild()}}else t.parent.addOrRebuild(t)}get isEditorHandlingKeyboard(){return this.getActive()?.shouldGetKeyboardEvents()||1===this.#at.size&&this.firstSelectedEditor.shouldGetKeyboardEvents()}isActive(t){return this.#k===t}getActive(){return this.#k}getMode(){return this.#nt}get imageManager(){return shadow(this,"imageManager",new ImageManager)}getSelectionBoxes(t){if(!t)return null;const e=document.getSelection();for(let i=0,s=e.rangeCount;i<s;i++)if(!t.contains(e.getRangeAt(i).commonAncestorContainer))return null;const{x:i,y:s,width:n,height:a}=t.getBoundingClientRect();let r;switch(t.getAttribute("data-main-rotation")){case"90":r=(t,e,r,o)=>({x:(e-s)/a,y:1-(t+r-i)/n,width:o/a,height:r/n});break;case"180":r=(t,e,r,o)=>({x:1-(t+r-i)/n,y:1-(e+o-s)/a,width:r/n,height:o/a});break;case"270":r=(t,e,r,o)=>({x:1-(e+o-s)/a,y:(t-i)/n,width:o/a,height:r/n});break;default:r=(t,e,r,o)=>({x:(t-i)/n,y:(e-s)/a,width:r/n,height:o/a})}const o=[];for(let t=0,i=e.rangeCount;t<i;t++){const i=e.getRangeAt(t);if(!i.collapsed)for(const{x:t,y:e,width:s,height:n}of i.getClientRects())0!==s&&0!==n&&o.push(r(t,e,s,n))}return 0===o.length?null:o}addChangedExistingAnnotation({annotationElementId:t,id:e}){(this.#I||=new Map).set(t,e)}removeChangedExistingAnnotation({annotationElementId:t}){this.#I?.delete(t)}renderAnnotationElement(t){const e=this.#I?.get(t.data.id);if(!e)return;const i=this.#R.getRawValue(e);i&&(this.#nt!==g.NONE||i.hasBeenModified)&&i.renderAnnotationElement(t)}}class AltText{#o=null;#Nt=!1;#Ot=null;#Bt=null;#Ht=null;#zt=!1;#Ut=null;#a=null;#jt=null;#Wt=null;#Gt=!1;static#$t=null;static _l10nPromise=null;constructor(t){this.#a=t;this.#Gt=t._uiManager.useNewAltTextFlow;AltText.#$t||=Object.freeze({added:"pdfjs-editor-new-alt-text-added-button-label",missing:"pdfjs-editor-new-alt-text-missing-button-label",review:"pdfjs-editor-new-alt-text-to-review-button-label"})}static initialize(t){AltText._l10nPromise||=t}async render(){const t=this.#Ot=document.createElement("button");t.className="altText";let e;if(this.#Gt){t.classList.add("new");e=await AltText._l10nPromise.get(AltText.#$t.missing)}else e=await AltText._l10nPromise.get("pdfjs-editor-alt-text-button-label");t.textContent=e;t.setAttribute("aria-label",e);t.tabIndex="0";const i=this.#a._uiManager._signal;t.addEventListener("contextmenu",noContextMenu,{signal:i});t.addEventListener("pointerdown",(t=>t.stopPropagation()),{signal:i});const onClick=t=>{t.preventDefault();this.#a._uiManager.editAltText(this.#a);this.#Gt&&this.#a._reportTelemetry({action:"pdfjs.image.alt_text.image_status_label_clicked",data:{label:this.#Vt}})};t.addEventListener("click",onClick,{capture:!0,signal:i});t.addEventListener("keydown",(e=>{if(e.target===t&&"Enter"===e.key){this.#zt=!0;onClick(e)}}),{signal:i});await this.#qt();return t}get#Vt(){return(this.#o?"added":null===this.#o&&this.guessedText&&"review")||"missing"}finish(){if(this.#Ot){this.#Ot.focus({focusVisible:this.#zt});this.#zt=!1}}isEmpty(){return this.#Gt?null===this.#o:!this.#o&&!this.#Nt}hasData(){return this.#Gt?null!==this.#o||!!this.#jt:this.isEmpty()}get guessedText(){return this.#jt}async setGuessedText(t){if(null===this.#o){this.#jt=t;this.#Wt=await AltText._l10nPromise.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer")({generatedAltText:t});this.#qt()}}toggleAltTextBadge(t=!1){if(this.#Gt&&!this.#o){if(!this.#Ut){const t=this.#Ut=document.createElement("div");t.className="noAltTextBadge";this.#a.div.append(t)}this.#Ut.classList.toggle("hidden",!t)}else{this.#Ut?.remove();this.#Ut=null}}serialize(t){let e=this.#o;t||this.#jt!==e||(e=this.#Wt);return{altText:e,decorative:this.#Nt,guessedText:this.#jt,textWithDisclaimer:this.#Wt}}get data(){return{altText:this.#o,decorative:this.#Nt}}set data({altText:t,decorative:e,guessedText:i,textWithDisclaimer:s,cancel:n=!1}){if(i){this.#jt=i;this.#Wt=s}if(this.#o!==t||this.#Nt!==e){if(!n){this.#o=t;this.#Nt=e}this.#qt()}}toggle(t=!1){if(this.#Ot){if(!t&&this.#Ht){clearTimeout(this.#Ht);this.#Ht=null}this.#Ot.disabled=!t}}shown(){this.#a._reportTelemetry({action:"pdfjs.image.alt_text.image_status_label_displayed",data:{label:this.#Vt}})}destroy(){this.#Ot?.remove();this.#Ot=null;this.#Bt=null;this.#Ut?.remove();this.#Ut=null}async#qt(){const t=this.#Ot;if(!t)return;if(this.#Gt){t.classList.toggle("done",!!this.#o);AltText._l10nPromise.get(AltText.#$t[this.#Vt]).then((e=>{t.setAttribute("aria-label",e);for(const i of t.childNodes)if(i.nodeType===Node.TEXT_NODE){i.textContent=e;break}}));if(!this.#o){this.#Bt?.remove();return}}else{if(!this.#o&&!this.#Nt){t.classList.remove("done");this.#Bt?.remove();return}t.classList.add("done");AltText._l10nPromise.get("pdfjs-editor-alt-text-edit-button-label").then((e=>{t.setAttribute("aria-label",e)}))}let e=this.#Bt;if(!e){this.#Bt=e=document.createElement("span");e.className="tooltip";e.setAttribute("role","tooltip");e.id=`alt-text-tooltip-${this.#a.id}`;const i=100,s=this.#a._uiManager._signal;s.addEventListener("abort",(()=>{clearTimeout(this.#Ht);this.#Ht=null}),{once:!0});t.addEventListener("mouseenter",(()=>{this.#Ht=setTimeout((()=>{this.#Ht=null;this.#Bt.classList.add("show");this.#a._reportTelemetry({action:"alt_text_tooltip"})}),i)}),{signal:s});t.addEventListener("mouseleave",(()=>{if(this.#Ht){clearTimeout(this.#Ht);this.#Ht=null}this.#Bt?.classList.remove("show")}),{signal:s})}e.innerText=this.#Nt?await AltText._l10nPromise.get("pdfjs-editor-alt-text-decorative-tooltip"):this.#o;e.parentNode||t.append(e);const i=this.#a.getImageForAltText();i?.setAttribute("aria-describedby",e.id)}}class AnnotationEditor{#Xt=null;#Kt=null;#o=null;#Yt=!1;#Qt=!1;#Jt=null;#Zt=null;#te=null;#ee="";#ie=!1;#se=null;#ne=!1;#ae=!1;#re=!1;#oe=null;#le=0;#he=0;#de=null;_editToolbar=null;_initialOptions=Object.create(null);_initialData=null;_isVisible=!0;_uiManager=null;_focusEventsAllowed=!0;static _l10nPromise=null;static _l10nResizer=null;#ce=!1;#ue=AnnotationEditor._zIndex++;static _borderLineWidth=-1;static _colorManager=new ColorManager;static _zIndex=1;static _telemetryTimeout=1e3;static get _resizerKeyboardManager(){const t=AnnotationEditor.prototype._resizeWithKeyboard,e=AnnotationEditorUIManager.TRANSLATE_SMALL,i=AnnotationEditorUIManager.TRANSLATE_BIG;return shadow(this,"_resizerKeyboardManager",new KeyboardManager([[["ArrowLeft","mac+ArrowLeft"],t,{args:[-e,0]}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t,{args:[-i,0]}],[["ArrowRight","mac+ArrowRight"],t,{args:[e,0]}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t,{args:[i,0]}],[["ArrowUp","mac+ArrowUp"],t,{args:[0,-e]}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t,{args:[0,-i]}],[["ArrowDown","mac+ArrowDown"],t,{args:[0,e]}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t,{args:[0,i]}],[["Escape","mac+Escape"],AnnotationEditor.prototype._stopResizingWithKeyboard]]))}constructor(t){this.parent=t.parent;this.id=t.id;this.width=this.height=null;this.pageIndex=t.parent.pageIndex;this.name=t.name;this.div=null;this._uiManager=t.uiManager;this.annotationElementId=null;this._willKeepAspectRatio=!1;this._initialOptions.isCentered=t.isCentered;this._structTreeParentId=null;const{rotation:e,rawDims:{pageWidth:i,pageHeight:s,pageX:n,pageY:a}}=this.parent.viewport;this.rotation=e;this.pageRotation=(360+e-this._uiManager.viewParameters.rotation)%360;this.pageDimensions=[i,s];this.pageTranslation=[n,a];const[r,o]=this.parentDimensions;this.x=t.x/r;this.y=t.y/o;this.isAttachedToDOM=!1;this.deleted=!1}get editorType(){return Object.getPrototypeOf(this).constructor._type}static get _defaultLineColor(){return shadow(this,"_defaultLineColor",this._colorManager.getHexCode("CanvasText"))}static deleteAnnotationElement(t){const e=new FakeEditor({id:t.parent.getNextId(),parent:t.parent,uiManager:t._uiManager});e.annotationElementId=t.annotationElementId;e.deleted=!0;e._uiManager.addToAnnotationStorage(e)}static initialize(t,e,i){AnnotationEditor._l10nResizer||=Object.freeze({topLeft:"pdfjs-editor-resizer-top-left",topMiddle:"pdfjs-editor-resizer-top-middle",topRight:"pdfjs-editor-resizer-top-right",middleRight:"pdfjs-editor-resizer-middle-right",bottomRight:"pdfjs-editor-resizer-bottom-right",bottomMiddle:"pdfjs-editor-resizer-bottom-middle",bottomLeft:"pdfjs-editor-resizer-bottom-left",middleLeft:"pdfjs-editor-resizer-middle-left"});AnnotationEditor._l10nPromise||=new Map([...["pdfjs-editor-alt-text-button-label","pdfjs-editor-alt-text-edit-button-label","pdfjs-editor-alt-text-decorative-tooltip","pdfjs-editor-new-alt-text-added-button-label","pdfjs-editor-new-alt-text-missing-button-label","pdfjs-editor-new-alt-text-to-review-button-label"].map((e=>[e,t.get(e)])),...["pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer"].map((e=>[e,t.get.bind(t,e)]))]);if(i?.strings)for(const e of i.strings)AnnotationEditor._l10nPromise.set(e,t.get(e));if(-1!==AnnotationEditor._borderLineWidth)return;const s=getComputedStyle(document.documentElement);AnnotationEditor._borderLineWidth=parseFloat(s.getPropertyValue("--outline-width"))||0}static updateDefaultParams(t,e){}static get defaultPropertiesToUpdate(){return[]}static isHandlingMimeForPasting(t){return!1}static paste(t,e){unreachable("Not implemented")}get propertiesToUpdate(){return[]}get _isDraggable(){return this.#ce}set _isDraggable(t){this.#ce=t;this.div?.classList.toggle("draggable",t)}get isEnterHandled(){return!0}center(){const[t,e]=this.pageDimensions;switch(this.parentRotation){case 90:this.x-=this.height*e/(2*t);this.y+=this.width*t/(2*e);break;case 180:this.x+=this.width/2;this.y+=this.height/2;break;case 270:this.x+=this.height*e/(2*t);this.y-=this.width*t/(2*e);break;default:this.x-=this.width/2;this.y-=this.height/2}this.fixAndSetPosition()}addCommands(t){this._uiManager.addCommands(t)}get currentLayer(){return this._uiManager.currentLayer}setInBackground(){this.div.style.zIndex=0}setInForeground(){this.div.style.zIndex=this.#ue}setParent(t){if(null!==t){this.pageIndex=t.pageIndex;this.pageDimensions=t.pageDimensions}else this.#pe();this.parent=t}focusin(t){this._focusEventsAllowed&&(this.#ie?this.#ie=!1:this.parent.setSelected(this))}focusout(t){if(!this._focusEventsAllowed)return;if(!this.isAttachedToDOM)return;const e=t.relatedTarget;if(!e?.closest(`#${this.id}`)){t.preventDefault();this.parent?.isMultipleSelection||this.commitOrRemove()}}commitOrRemove(){this.isEmpty()?this.remove():this.commit()}commit(){this.addToAnnotationStorage()}addToAnnotationStorage(){this._uiManager.addToAnnotationStorage(this)}setAt(t,e,i,s){const[n,a]=this.parentDimensions;[i,s]=this.screenToPageTranslation(i,s);this.x=(t+i)/n;this.y=(e+s)/a;this.fixAndSetPosition()}#ge([t,e],i,s){[i,s]=this.screenToPageTranslation(i,s);this.x+=i/t;this.y+=s/e;this.fixAndSetPosition()}translate(t,e){this.#ge(this.parentDimensions,t,e)}translateInPage(t,e){this.#se||=[this.x,this.y];this.#ge(this.pageDimensions,t,e);this.div.scrollIntoView({block:"nearest"})}drag(t,e){this.#se||=[this.x,this.y];const[i,s]=this.parentDimensions;this.x+=t/i;this.y+=e/s;if(this.parent&&(this.x<0||this.x>1||this.y<0||this.y>1)){const{x:t,y:e}=this.div.getBoundingClientRect();if(this.parent.findNewParent(this,t,e)){this.x-=Math.floor(this.x);this.y-=Math.floor(this.y)}}let{x:n,y:a}=this;const[r,o]=this.getBaseTranslation();n+=r;a+=o;this.div.style.left=`${(100*n).toFixed(2)}%`;this.div.style.top=`${(100*a).toFixed(2)}%`;this.div.scrollIntoView({block:"nearest"})}get _hasBeenMoved(){return!!this.#se&&(this.#se[0]!==this.x||this.#se[1]!==this.y)}getBaseTranslation(){const[t,e]=this.parentDimensions,{_borderLineWidth:i}=AnnotationEditor,s=i/t,n=i/e;switch(this.rotation){case 90:return[-s,n];case 180:return[s,n];case 270:return[s,-n];default:return[-s,-n]}}get _mustFixPosition(){return!0}fixAndSetPosition(t=this.rotation){const[e,i]=this.pageDimensions;let{x:s,y:n,width:a,height:r}=this;a*=e;r*=i;s*=e;n*=i;if(this._mustFixPosition)switch(t){case 0:s=Math.max(0,Math.min(e-a,s));n=Math.max(0,Math.min(i-r,n));break;case 90:s=Math.max(0,Math.min(e-r,s));n=Math.min(i,Math.max(a,n));break;case 180:s=Math.min(e,Math.max(a,s));n=Math.min(i,Math.max(r,n));break;case 270:s=Math.min(e,Math.max(r,s));n=Math.max(0,Math.min(i-a,n))}this.x=s/=e;this.y=n/=i;const[o,l]=this.getBaseTranslation();s+=o;n+=l;const{style:h}=this.div;h.left=`${(100*s).toFixed(2)}%`;h.top=`${(100*n).toFixed(2)}%`;this.moveInDOM()}static#me(t,e,i){switch(i){case 90:return[e,-t];case 180:return[-t,-e];case 270:return[-e,t];default:return[t,e]}}screenToPageTranslation(t,e){return AnnotationEditor.#me(t,e,this.parentRotation)}pageTranslationToScreen(t,e){return AnnotationEditor.#me(t,e,360-this.parentRotation)}#fe(t){switch(t){case 90:{const[t,e]=this.pageDimensions;return[0,-t/e,e/t,0]}case 180:return[-1,0,0,-1];case 270:{const[t,e]=this.pageDimensions;return[0,t/e,-e/t,0]}default:return[1,0,0,1]}}get parentScale(){return this._uiManager.viewParameters.realScale}get parentRotation(){return(this._uiManager.viewParameters.rotation+this.pageRotation)%360}get parentDimensions(){const{parentScale:t,pageDimensions:[e,i]}=this;return[e*t,i*t]}setDims(t,e){const[i,s]=this.parentDimensions;this.div.style.width=`${(100*t/i).toFixed(2)}%`;this.#Qt||(this.div.style.height=`${(100*e/s).toFixed(2)}%`)}fixDims(){const{style:t}=this.div,{height:e,width:i}=t,s=i.endsWith("%"),n=!this.#Qt&&e.endsWith("%");if(s&&n)return;const[a,r]=this.parentDimensions;s||(t.width=`${(100*parseFloat(i)/a).toFixed(2)}%`);this.#Qt||n||(t.height=`${(100*parseFloat(e)/r).toFixed(2)}%`)}getInitialTranslation(){return[0,0]}#be(){if(this.#Jt)return;this.#Jt=document.createElement("div");this.#Jt.classList.add("resizers");const t=this._willKeepAspectRatio?["topLeft","topRight","bottomRight","bottomLeft"]:["topLeft","topMiddle","topRight","middleRight","bottomRight","bottomMiddle","bottomLeft","middleLeft"],e=this._uiManager._signal;for(const i of t){const t=document.createElement("div");this.#Jt.append(t);t.classList.add("resizer",i);t.setAttribute("data-resizer-name",i);t.addEventListener("pointerdown",this.#Ae.bind(this,i),{signal:e});t.addEventListener("contextmenu",noContextMenu,{signal:e});t.tabIndex=-1}this.div.prepend(this.#Jt)}#Ae(t,e){e.preventDefault();const{isMac:i}=util_FeatureTest.platform;if(0!==e.button||e.ctrlKey&&i)return;this.#o?.toggle(!1);const s=this._isDraggable;this._isDraggable=!1;const n=new AbortController,a=this._uiManager.combinedSignal(n);this.parent.togglePointerEvents(!1);window.addEventListener("pointermove",this.#ve.bind(this,t),{passive:!0,capture:!0,signal:a});window.addEventListener("contextmenu",noContextMenu,{signal:a});const r=this.x,o=this.y,l=this.width,h=this.height,d=this.parent.div.style.cursor,c=this.div.style.cursor;this.div.style.cursor=this.parent.div.style.cursor=window.getComputedStyle(e.target).cursor;const pointerUpCallback=()=>{n.abort();this.parent.togglePointerEvents(!0);this.#o?.toggle(!0);this._isDraggable=s;this.parent.div.style.cursor=d;this.div.style.cursor=c;this.#ye(r,o,l,h)};window.addEventListener("pointerup",pointerUpCallback,{signal:a});window.addEventListener("blur",pointerUpCallback,{signal:a})}#ye(t,e,i,s){const n=this.x,a=this.y,r=this.width,o=this.height;n===t&&a===e&&r===i&&o===s||this.addCommands({cmd:()=>{this.width=r;this.height=o;this.x=n;this.y=a;const[t,e]=this.parentDimensions;this.setDims(t*r,e*o);this.fixAndSetPosition()},undo:()=>{this.width=i;this.height=s;this.x=t;this.y=e;const[n,a]=this.parentDimensions;this.setDims(n*i,a*s);this.fixAndSetPosition()},mustExec:!0})}#ve(t,e){const[i,s]=this.parentDimensions,n=this.x,a=this.y,r=this.width,o=this.height,l=AnnotationEditor.MIN_SIZE/i,h=AnnotationEditor.MIN_SIZE/s,round=t=>Math.round(1e4*t)/1e4,d=this.#fe(this.rotation),transf=(t,e)=>[d[0]*t+d[2]*e,d[1]*t+d[3]*e],c=this.#fe(360-this.rotation);let u,p,g=!1,m=!1;switch(t){case"topLeft":g=!0;u=(t,e)=>[0,0];p=(t,e)=>[t,e];break;case"topMiddle":u=(t,e)=>[t/2,0];p=(t,e)=>[t/2,e];break;case"topRight":g=!0;u=(t,e)=>[t,0];p=(t,e)=>[0,e];break;case"middleRight":m=!0;u=(t,e)=>[t,e/2];p=(t,e)=>[0,e/2];break;case"bottomRight":g=!0;u=(t,e)=>[t,e];p=(t,e)=>[0,0];break;case"bottomMiddle":u=(t,e)=>[t/2,e];p=(t,e)=>[t/2,0];break;case"bottomLeft":g=!0;u=(t,e)=>[0,e];p=(t,e)=>[t,0];break;case"middleLeft":m=!0;u=(t,e)=>[0,e/2];p=(t,e)=>[t,e/2]}const f=u(r,o),b=p(r,o);let A=transf(...b);const v=round(n+A[0]),y=round(a+A[1]);let w=1,x=1,[_,E]=this.screenToPageTranslation(e.movementX,e.movementY);[_,E]=(C=_/i,S=E/s,[c[0]*C+c[2]*S,c[1]*C+c[3]*S]);var C,S;if(g){const t=Math.hypot(r,o);w=x=Math.max(Math.min(Math.hypot(b[0]-f[0]-_,b[1]-f[1]-E)/t,1/r,1/o),l/r,h/o)}else m?w=Math.max(l,Math.min(1,Math.abs(b[0]-f[0]-_)))/r:x=Math.max(h,Math.min(1,Math.abs(b[1]-f[1]-E)))/o;const T=round(r*w),M=round(o*x);A=transf(...p(T,M));const k=v-A[0],P=y-A[1];this.width=T;this.height=M;this.x=k;this.y=P;this.setDims(i*T,s*M);this.fixAndSetPosition()}altTextFinish(){this.#o?.finish()}async addEditToolbar(){if(this._editToolbar||this.#ae)return this._editToolbar;this._editToolbar=new EditorToolbar(this);this.div.append(this._editToolbar.render());this.#o&&await this._editToolbar.addAltText(this.#o);return this._editToolbar}removeEditToolbar(){if(this._editToolbar){this._editToolbar.remove();this._editToolbar=null;this.#o?.destroy()}}addContainer(t){const e=this._editToolbar?.div;e?e.before(t):this.div.append(t)}getClientDimensions(){return this.div.getBoundingClientRect()}async addAltTextButton(){if(!this.#o){AltText.initialize(AnnotationEditor._l10nPromise);this.#o=new AltText(this);if(this.#Xt){this.#o.data=this.#Xt;this.#Xt=null}await this.addEditToolbar()}}get altTextData(){return this.#o?.data}set altTextData(t){this.#o&&(this.#o.data=t)}get guessedAltText(){return this.#o?.guessedText}async setGuessedAltText(t){await(this.#o?.setGuessedText(t))}serializeAltText(t){return this.#o?.serialize(t)}hasAltText(){return!!this.#o&&!this.#o.isEmpty()}hasAltTextData(){return this.#o?.hasData()??!1}render(){this.div=document.createElement("div");this.div.setAttribute("data-editor-rotation",(360-this.rotation)%360);this.div.className=this.name;this.div.setAttribute("id",this.id);this.div.tabIndex=this.#Yt?-1:0;this._isVisible||this.div.classList.add("hidden");this.setInForeground();this.#we();const[t,e]=this.parentDimensions;if(this.parentRotation%180!=0){this.div.style.maxWidth=`${(100*e/t).toFixed(2)}%`;this.div.style.maxHeight=`${(100*t/e).toFixed(2)}%`}const[i,s]=this.getInitialTranslation();this.translate(i,s);bindEvents(this,this.div,["pointerdown"]);return this.div}pointerdown(t){const{isMac:e}=util_FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)t.preventDefault();else{this.#ie=!0;this._isDraggable?this.#xe(t):this.#_e(t)}}#_e(t){const{isMac:e}=util_FeatureTest.platform;t.ctrlKey&&!e||t.shiftKey||t.metaKey&&e?this.parent.toggleSelected(this):this.parent.setSelected(this)}#xe(t){const e=this._uiManager.isSelected(this);this._uiManager.setUpDragSession();const i=new AbortController,s=this._uiManager.combinedSignal(i);if(e){this.div.classList.add("moving");this.#le=t.clientX;this.#he=t.clientY;const pointerMoveCallback=t=>{const{clientX:e,clientY:i}=t,[s,n]=this.screenToPageTranslation(e-this.#le,i-this.#he);this.#le=e;this.#he=i;this._uiManager.dragSelectedEditors(s,n)};window.addEventListener("pointermove",pointerMoveCallback,{passive:!0,capture:!0,signal:s})}const pointerUpCallback=()=>{i.abort();e&&this.div.classList.remove("moving");this.#ie=!1;this._uiManager.endDragSession()||this.#_e(t)};window.addEventListener("pointerup",pointerUpCallback,{signal:s});window.addEventListener("blur",pointerUpCallback,{signal:s})}moveInDOM(){this.#oe&&clearTimeout(this.#oe);this.#oe=setTimeout((()=>{this.#oe=null;this.parent?.moveEditorInDOM(this)}),0)}_setParentAndPosition(t,e,i){t.changeParent(this);this.x=e;this.y=i;this.fixAndSetPosition()}getRect(t,e,i=this.rotation){const s=this.parentScale,[n,a]=this.pageDimensions,[r,o]=this.pageTranslation,l=t/s,h=e/s,d=this.x*n,c=this.y*a,u=this.width*n,p=this.height*a;switch(i){case 0:return[d+l+r,a-c-h-p+o,d+l+u+r,a-c-h+o];case 90:return[d+h+r,a-c+l+o,d+h+p+r,a-c+l+u+o];case 180:return[d-l-u+r,a-c+h+o,d-l+r,a-c+h+p+o];case 270:return[d-h-p+r,a-c-l-u+o,d-h+r,a-c-l+o];default:throw new Error("Invalid rotation")}}getRectInCurrentCoords(t,e){const[i,s,n,a]=t,r=n-i,o=a-s;switch(this.rotation){case 0:return[i,e-a,r,o];case 90:return[i,e-s,o,r];case 180:return[n,e-s,r,o];case 270:return[n,e-a,o,r];default:throw new Error("Invalid rotation")}}onceAdded(){}isEmpty(){return!1}enableEditMode(){this.#ae=!0}disableEditMode(){this.#ae=!1}isInEditMode(){return this.#ae}shouldGetKeyboardEvents(){return this.#re}needsToBeRebuilt(){return this.div&&!this.isAttachedToDOM}#we(){if(this.#te||!this.div)return;this.#te=new AbortController;const t=this._uiManager.combinedSignal(this.#te);this.div.addEventListener("focusin",this.focusin.bind(this),{signal:t});this.div.addEventListener("focusout",this.focusout.bind(this),{signal:t})}rebuild(){this.#we()}rotate(t){}serializeDeleted(){return{id:this.annotationElementId,deleted:!0,pageIndex:this.pageIndex,popupRef:this._initialData?.popupRef||""}}serialize(t=!1,e=null){unreachable("An editor must be serializable")}static async deserialize(t,e,i){const s=new this.prototype.constructor({parent:e,id:e.getNextId(),uiManager:i});s.rotation=t.rotation;s.#Xt=t.accessibilityData;const[n,a]=s.pageDimensions,[r,o,l,h]=s.getRectInCurrentCoords(t.rect,a);s.x=r/n;s.y=o/a;s.width=l/n;s.height=h/a;return s}get hasBeenModified(){return!!this.annotationElementId&&(this.deleted||null!==this.serialize())}remove(){this.#te?.abort();this.#te=null;this.isEmpty()||this.commit();this.parent?this.parent.remove(this):this._uiManager.removeEditor(this);if(this.#oe){clearTimeout(this.#oe);this.#oe=null}this.#pe();this.removeEditToolbar();if(this.#de){for(const t of this.#de.values())clearTimeout(t);this.#de=null}this.parent=null}get isResizable(){return!1}makeResizable(){if(this.isResizable){this.#be();this.#Jt.classList.remove("hidden");bindEvents(this,this.div,["keydown"])}}get toolbarPosition(){return null}keydown(t){if(!this.isResizable||t.target!==this.div||"Enter"!==t.key)return;this._uiManager.setSelected(this);this.#Zt={savedX:this.x,savedY:this.y,savedWidth:this.width,savedHeight:this.height};const e=this.#Jt.children;if(!this.#Kt){this.#Kt=Array.from(e);const t=this.#Ee.bind(this),i=this.#Ce.bind(this),s=this._uiManager._signal;for(const e of this.#Kt){const n=e.getAttribute("data-resizer-name");e.setAttribute("role","spinbutton");e.addEventListener("keydown",t,{signal:s});e.addEventListener("blur",i,{signal:s});e.addEventListener("focus",this.#Se.bind(this,n),{signal:s});e.setAttribute("data-l10n-id",AnnotationEditor._l10nResizer[n])}}const i=this.#Kt[0];let s=0;for(const t of e){if(t===i)break;s++}const n=(360-this.rotation+this.parentRotation)%360/90*(this.#Kt.length/4);if(n!==s){if(n<s)for(let t=0;t<s-n;t++)this.#Jt.append(this.#Jt.firstChild);else if(n>s)for(let t=0;t<n-s;t++)this.#Jt.firstChild.before(this.#Jt.lastChild);let t=0;for(const i of e){const e=this.#Kt[t++].getAttribute("data-resizer-name");i.setAttribute("data-l10n-id",AnnotationEditor._l10nResizer[e])}}this.#Te(0);this.#re=!0;this.#Jt.firstChild.focus({focusVisible:!0});t.preventDefault();t.stopImmediatePropagation()}#Ee(t){AnnotationEditor._resizerKeyboardManager.exec(this,t)}#Ce(t){this.#re&&t.relatedTarget?.parentNode!==this.#Jt&&this.#pe()}#Se(t){this.#ee=this.#re?t:""}#Te(t){if(this.#Kt)for(const e of this.#Kt)e.tabIndex=t}_resizeWithKeyboard(t,e){this.#re&&this.#ve(this.#ee,{movementX:t,movementY:e})}#pe(){this.#re=!1;this.#Te(-1);if(this.#Zt){const{savedX:t,savedY:e,savedWidth:i,savedHeight:s}=this.#Zt;this.#ye(t,e,i,s);this.#Zt=null}}_stopResizingWithKeyboard(){this.#pe();this.div.focus()}select(){this.makeResizable();this.div?.classList.add("selectedEditor");if(this._editToolbar){this._editToolbar?.show();this.#o?.toggleAltTextBadge(!1)}else this.addEditToolbar().then((()=>{this.div?.classList.contains("selectedEditor")&&this._editToolbar?.show()}))}unselect(){this.#Jt?.classList.add("hidden");this.div?.classList.remove("selectedEditor");this.div?.contains(document.activeElement)&&this._uiManager.currentLayer.div.focus({preventScroll:!0});this._editToolbar?.hide();this.#o?.toggleAltTextBadge(!0)}updateParams(t,e){}disableEditing(){}enableEditing(){}enterInEditMode(){}getImageForAltText(){return null}get contentDiv(){return this.div}get isEditing(){return this.#ne}set isEditing(t){this.#ne=t;if(this.parent)if(t){this.parent.setSelected(this);this.parent.setActiveEditor(this)}else this.parent.setActiveEditor(null)}setAspectRatio(t,e){this.#Qt=!0;const i=t/e,{style:s}=this.div;s.aspectRatio=i;s.height="auto"}static get MIN_SIZE(){return 16}static canCreateNewEmptyEditor(){return!0}get telemetryInitialData(){return{action:"added"}}get telemetryFinalData(){return null}_reportTelemetry(t,e=!1){if(e){this.#de||=new Map;const{action:e}=t;let i=this.#de.get(e);i&&clearTimeout(i);i=setTimeout((()=>{this._reportTelemetry(t);this.#de.delete(e);0===this.#de.size&&(this.#de=null)}),AnnotationEditor._telemetryTimeout);this.#de.set(e,i)}else{t.type||=this.editorType;this._uiManager._eventBus.dispatch("reporttelemetry",{source:this,details:{type:"editing",data:t}})}}show(t=this._isVisible){this.div.classList.toggle("hidden",!t);this._isVisible=t}enable(){this.div&&(this.div.tabIndex=0);this.#Yt=!1}disable(){this.div&&(this.div.tabIndex=-1);this.#Yt=!0}renderAnnotationElement(t){let e=t.container.querySelector(".annotationContent");if(e){if("CANVAS"===e.nodeName){const t=e;e=document.createElement("div");e.classList.add("annotationContent",this.editorType);t.before(e)}}else{e=document.createElement("div");e.classList.add("annotationContent",this.editorType);t.container.prepend(e)}return e}resetAnnotationElement(t){const{firstChild:e}=t.container;"DIV"===e?.nodeName&&e.classList.contains("annotationContent")&&e.remove()}}class FakeEditor extends AnnotationEditor{constructor(t){super(t);this.annotationElementId=t.annotationElementId;this.deleted=!0}serialize(){return this.serializeDeleted()}}const gt=3285377520,mt=4294901760,ft=65535;class MurmurHash3_64{constructor(t){this.h1=t?4294967295&t:gt;this.h2=t?4294967295&t:gt}update(t){let e,i;if("string"==typeof t){e=new Uint8Array(2*t.length);i=0;for(let s=0,n=t.length;s<n;s++){const n=t.charCodeAt(s);if(n<=255)e[i++]=n;else{e[i++]=n>>>8;e[i++]=255&n}}}else{if(!ArrayBuffer.isView(t))throw new Error("Invalid data format, must be a string or TypedArray.");e=t.slice();i=e.byteLength}const s=i>>2,n=i-4*s,a=new Uint32Array(e.buffer,0,s);let r=0,o=0,l=this.h1,h=this.h2;const d=3432918353,c=461845907,u=11601,p=13715;for(let t=0;t<s;t++)if(1&t){r=a[t];r=r*d&mt|r*u&ft;r=r<<15|r>>>17;r=r*c&mt|r*p&ft;l^=r;l=l<<13|l>>>19;l=5*l+3864292196}else{o=a[t];o=o*d&mt|o*u&ft;o=o<<15|o>>>17;o=o*c&mt|o*p&ft;h^=o;h=h<<13|h>>>19;h=5*h+3864292196}r=0;switch(n){case 3:r^=e[4*s+2]<<16;case 2:r^=e[4*s+1]<<8;case 1:r^=e[4*s];r=r*d&mt|r*u&ft;r=r<<15|r>>>17;r=r*c&mt|r*p&ft;1&s?l^=r:h^=r}this.h1=l;this.h2=h}hexdigest(){let t=this.h1,e=this.h2;t^=e>>>1;t=3981806797*t&mt|36045*t&ft;e=4283543511*e&mt|(2950163797*(e<<16|t>>>16)&mt)>>>16;t^=e>>>1;t=444984403*t&mt|60499*t&ft;e=3301882366*e&mt|(3120437893*(e<<16|t>>>16)&mt)>>>16;t^=e>>>1;return(t>>>0).toString(16).padStart(8,"0")+(e>>>0).toString(16).padStart(8,"0")}}const bt=Object.freeze({map:null,hash:"",transfer:void 0});class AnnotationStorage{#Me=!1;#ke=null;#Pe=new Map;constructor(){this.onSetModified=null;this.onResetModified=null;this.onAnnotationEditor=null}getValue(t,e){const i=this.#Pe.get(t);return void 0===i?e:Object.assign(e,i)}getRawValue(t){return this.#Pe.get(t)}remove(t){this.#Pe.delete(t);0===this.#Pe.size&&this.resetModified();if("function"==typeof this.onAnnotationEditor){for(const t of this.#Pe.values())if(t instanceof AnnotationEditor)return;this.onAnnotationEditor(null)}}setValue(t,e){const i=this.#Pe.get(t);let s=!1;if(void 0!==i){for(const[t,n]of Object.entries(e))if(i[t]!==n){s=!0;i[t]=n}}else{s=!0;this.#Pe.set(t,e)}s&&this.#Fe();e instanceof AnnotationEditor&&"function"==typeof this.onAnnotationEditor&&this.onAnnotationEditor(e.constructor._type)}has(t){return this.#Pe.has(t)}getAll(){return this.#Pe.size>0?objectFromMap(this.#Pe):null}setAll(t){for(const[e,i]of Object.entries(t))this.setValue(e,i)}get size(){return this.#Pe.size}#Fe(){if(!this.#Me){this.#Me=!0;"function"==typeof this.onSetModified&&this.onSetModified()}}resetModified(){if(this.#Me){this.#Me=!1;"function"==typeof this.onResetModified&&this.onResetModified()}}get print(){return new PrintAnnotationStorage(this)}get serializable(){if(0===this.#Pe.size)return bt;const t=new Map,e=new MurmurHash3_64,i=[],s=Object.create(null);let n=!1;for(const[i,a]of this.#Pe){const r=a instanceof AnnotationEditor?a.serialize(!1,s):a;if(r){t.set(i,r);e.update(`${i}:${JSON.stringify(r)}`);n||=!!r.bitmap}}if(n)for(const e of t.values())e.bitmap&&i.push(e.bitmap);return t.size>0?{map:t,hash:e.hexdigest(),transfer:i}:bt}get editorStats(){let t=null;const e=new Map;for(const i of this.#Pe.values()){if(!(i instanceof AnnotationEditor))continue;const s=i.telemetryFinalData;if(!s)continue;const{type:n}=s;e.has(n)||e.set(n,Object.getPrototypeOf(i).constructor);t||=Object.create(null);const a=t[n]||=new Map;for(const[t,e]of Object.entries(s)){if("type"===t)continue;let i=a.get(t);if(!i){i=new Map;a.set(t,i)}const s=i.get(e)??0;i.set(e,s+1)}}for(const[i,s]of e)t[i]=s.computeTelemetryFinalData(t[i]);return t}resetModifiedIds(){this.#ke=null}get modifiedIds(){if(this.#ke)return this.#ke;const t=[];for(const e of this.#Pe.values())e instanceof AnnotationEditor&&e.annotationElementId&&e.serialize()&&t.push(e.annotationElementId);return this.#ke={ids:new Set(t),hash:t.join(",")}}}class PrintAnnotationStorage extends AnnotationStorage{#De;constructor(t){super();const{map:e,hash:i,transfer:s}=t.serializable,n=structuredClone(e,s?{transfer:s}:null);this.#De={map:n,hash:i,transfer:s}}get print(){unreachable("Should not call PrintAnnotationStorage.print")}get serializable(){return this.#De}get modifiedIds(){return shadow(this,"modifiedIds",{ids:new Set,hash:""})}}class FontLoader{#Re=new Set;constructor({ownerDocument:t=globalThis.document,styleElement:e=null}){this._document=t;this.nativeFontFaces=new Set;this.styleElement=null;this.loadingRequests=[];this.loadTestFontId=0}addNativeFontFace(t){this.nativeFontFaces.add(t);this._document.fonts.add(t)}removeNativeFontFace(t){this.nativeFontFaces.delete(t);this._document.fonts.delete(t)}insertRule(t){if(!this.styleElement){this.styleElement=this._document.createElement("style");this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement)}const e=this.styleElement.sheet;e.insertRule(t,e.cssRules.length)}clear(){for(const t of this.nativeFontFaces)this._document.fonts.delete(t);this.nativeFontFaces.clear();this.#Re.clear();if(this.styleElement){this.styleElement.remove();this.styleElement=null}}async loadSystemFont({systemFontInfo:t,_inspectFont:e}){if(t&&!this.#Re.has(t.loadedName)){assert(!this.disableFontFace,"loadSystemFont shouldn't be called when `disableFontFace` is set.");if(this.isFontLoadingAPISupported){const{loadedName:i,src:s,style:n}=t,a=new FontFace(i,s,n);this.addNativeFontFace(a);try{await a.load();this.#Re.add(i);e?.(t)}catch{warn(`Cannot load system font: ${t.baseFontName}, installing it could help to improve PDF rendering.`);this.removeNativeFontFace(a)}}else unreachable("Not implemented: loadSystemFont without the Font Loading API.")}}async bind(t){if(t.attached||t.missingFile&&!t.systemFontInfo)return;t.attached=!0;if(t.systemFontInfo){await this.loadSystemFont(t);return}if(this.isFontLoadingAPISupported){const e=t.createNativeFontFace();if(e){this.addNativeFontFace(e);try{await e.loaded}catch(i){warn(`Failed to load font '${e.family}': '${i}'.`);t.disableFontFace=!0;throw i}}return}const e=t.createFontFaceRule();if(e){this.insertRule(e);if(this.isSyncFontLoadingSupported)return;await new Promise((e=>{const i=this._queueLoadingCallback(e);this._prepareFontLoadEvent(t,i)}))}}get isFontLoadingAPISupported(){return shadow(this,"isFontLoadingAPISupported",!!this._document?.fonts)}get isSyncFontLoadingSupported(){let t=!1;(e||"undefined"!=typeof navigator&&"string"==typeof navigator?.userAgent&&/Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent))&&(t=!0);return shadow(this,"isSyncFontLoadingSupported",t)}_queueLoadingCallback(t){const{loadingRequests:e}=this,i={done:!1,complete:function completeRequest(){assert(!i.done,"completeRequest() cannot be called twice.");i.done=!0;for(;e.length>0&&e[0].done;){const t=e.shift();setTimeout(t.callback,0)}},callback:t};e.push(i);return i}get _loadTestFont(){return shadow(this,"_loadTestFont",atob("T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA=="))}_prepareFontLoadEvent(t,e){function int32(t,e){return t.charCodeAt(e)<<24|t.charCodeAt(e+1)<<16|t.charCodeAt(e+2)<<8|255&t.charCodeAt(e+3)}function spliceString(t,e,i,s){return t.substring(0,e)+s+t.substring(e+i)}let i,s;const n=this._document.createElement("canvas");n.width=1;n.height=1;const a=n.getContext("2d");let r=0;const o=`lt${Date.now()}${this.loadTestFontId++}`;let l=this._loadTestFont;l=spliceString(l,976,o.length,o);const h=1482184792;let d=int32(l,16);for(i=0,s=o.length-3;i<s;i+=4)d=d-h+int32(o,i)|0;i<o.length&&(d=d-h+int32(o+"XXX",i)|0);l=spliceString(l,16,4,function string32(t){return String.fromCharCode(t>>24&255,t>>16&255,t>>8&255,255&t)}(d));const c=`@font-face {font-family:"${o}";src:${`url(data:font/opentype;base64,${btoa(l)});`}}`;this.insertRule(c);const u=this._document.createElement("div");u.style.visibility="hidden";u.style.width=u.style.height="10px";u.style.position="absolute";u.style.top=u.style.left="0px";for(const e of[t.loadedName,o]){const t=this._document.createElement("span");t.textContent="Hi";t.style.fontFamily=e;u.append(t)}this._document.body.append(u);!function isFontReady(t,e){if(++r>30){warn("Load test font never loaded.");e();return}a.font="30px "+t;a.fillText(".",0,20);a.getImageData(0,0,1,1).data[3]>0?e():setTimeout(isFontReady.bind(null,t,e))}(o,(()=>{u.remove();e.complete()}))}}class FontFaceObject{constructor(t,{disableFontFace:e=!1,inspectFont:i=null}){this.compiledGlyphs=Object.create(null);for(const e in t)this[e]=t[e];this.disableFontFace=!0===e;this._inspectFont=i}createNativeFontFace(){if(!this.data||this.disableFontFace)return null;let t;if(this.cssFontInfo){const e={weight:this.cssFontInfo.fontWeight};this.cssFontInfo.italicAngle&&(e.style=`oblique ${this.cssFontInfo.italicAngle}deg`);t=new FontFace(this.cssFontInfo.fontFamily,this.data,e)}else t=new FontFace(this.loadedName,this.data,{});this._inspectFont?.(this);return t}createFontFaceRule(){if(!this.data||this.disableFontFace)return null;const t=bytesToString(this.data),e=`url(data:${this.mimetype};base64,${btoa(t)});`;let i;if(this.cssFontInfo){let t=`font-weight: ${this.cssFontInfo.fontWeight};`;this.cssFontInfo.italicAngle&&(t+=`font-style: oblique ${this.cssFontInfo.italicAngle}deg;`);i=`@font-face {font-family:"${this.cssFontInfo.fontFamily}";${t}src:${e}}`}else i=`@font-face {font-family:"${this.loadedName}";src:${e}}`;this._inspectFont?.(this,e);return i}getPathGenerator(t,e){if(void 0!==this.compiledGlyphs[e])return this.compiledGlyphs[e];let i;try{i=t.get(this.loadedName+"_path_"+e)}catch(t){warn(`getPathGenerator - ignoring character: "${t}".`)}if(!Array.isArray(i)||0===i.length)return this.compiledGlyphs[e]=function(t,e){};const s=[];for(let t=0,e=i.length;t<e;)switch(i[t++]){case st:{const[e,n,a,r,o,l]=i.slice(t,t+6);s.push((t=>t.bezierCurveTo(e,n,a,r,o,l)));t+=6}break;case nt:{const[e,n]=i.slice(t,t+2);s.push((t=>t.moveTo(e,n)));t+=2}break;case at:{const[e,n]=i.slice(t,t+2);s.push((t=>t.lineTo(e,n)));t+=2}break;case rt:{const[e,n,a,r]=i.slice(t,t+4);s.push((t=>t.quadraticCurveTo(e,n,a,r)));t+=4}break;case ot:s.push((t=>t.restore()));break;case lt:s.push((t=>t.save()));break;case ht:assert(2===s.length,"Scale command is only valid at the third position.");break;case dt:{const[e,n,a,r,o,l]=i.slice(t,t+6);s.push((t=>t.transform(e,n,a,r,o,l)));t+=6}break;case ct:{const[e,n]=i.slice(t,t+2);s.push((t=>t.translate(e,n)));t+=2}}return this.compiledGlyphs[e]=function glyphDrawer(t,e){s[0](t);s[1](t);t.scale(e,-e);for(let e=2,i=s.length;e<i;e++)s[e](t)}}}if(e){var At=Promise.withResolvers(),vt=null;(async()=>{const t=await import("fs"),e=await import("http"),i=await import("https"),s=await import("url");return new Map(Object.entries({fs:t,http:e,https:i,url:s,canvas:undefined,path2d:undefined}))})().then((t=>{vt=t;At.resolve()}),(t=>{warn(`loadPackages: ${t}`);vt=new Map;At.resolve()}))}class NodePackages{static get promise(){return At.promise}static get(t){return vt?.get(t)}}const node_utils_fetchData=function(t){return NodePackages.get("fs").promises.readFile(t).then((t=>new Uint8Array(t)))};const yt="Fill",wt="Stroke",xt="Shading";function applyBoundingBox(t,e){if(!e)return;const i=e[2]-e[0],s=e[3]-e[1],n=new Path2D;n.rect(e[0],e[1],i,s);t.clip(n)}class BaseShadingPattern{getPattern(){unreachable("Abstract method `getPattern` called.")}}class RadialAxialShadingPattern extends BaseShadingPattern{constructor(t){super();this._type=t[1];this._bbox=t[2];this._colorStops=t[3];this._p0=t[4];this._p1=t[5];this._r0=t[6];this._r1=t[7];this.matrix=null}_createGradient(t){let e;"axial"===this._type?e=t.createLinearGradient(this._p0[0],this._p0[1],this._p1[0],this._p1[1]):"radial"===this._type&&(e=t.createRadialGradient(this._p0[0],this._p0[1],this._r0,this._p1[0],this._p1[1],this._r1));for(const t of this._colorStops)e.addColorStop(t[0],t[1]);return e}getPattern(t,e,i,s){let n;if(s===wt||s===yt){const a=e.current.getClippedPathBoundingBox(s,getCurrentTransform(t))||[0,0,0,0],r=Math.ceil(a[2]-a[0])||1,o=Math.ceil(a[3]-a[1])||1,l=e.cachedCanvases.getCanvas("pattern",r,o),h=l.context;h.clearRect(0,0,h.canvas.width,h.canvas.height);h.beginPath();h.rect(0,0,h.canvas.width,h.canvas.height);h.translate(-a[0],-a[1]);i=Util.transform(i,[1,0,0,1,a[0],a[1]]);h.transform(...e.baseTransform);this.matrix&&h.transform(...this.matrix);applyBoundingBox(h,this._bbox);h.fillStyle=this._createGradient(h);h.fill();n=t.createPattern(l.canvas,"no-repeat");const d=new DOMMatrix(i);n.setTransform(d)}else{applyBoundingBox(t,this._bbox);n=this._createGradient(t)}return n}}function drawTriangle(t,e,i,s,n,a,r,o){const l=e.coords,h=e.colors,d=t.data,c=4*t.width;let u;if(l[i+1]>l[s+1]){u=i;i=s;s=u;u=a;a=r;r=u}if(l[s+1]>l[n+1]){u=s;s=n;n=u;u=r;r=o;o=u}if(l[i+1]>l[s+1]){u=i;i=s;s=u;u=a;a=r;r=u}const p=(l[i]+e.offsetX)*e.scaleX,g=(l[i+1]+e.offsetY)*e.scaleY,m=(l[s]+e.offsetX)*e.scaleX,f=(l[s+1]+e.offsetY)*e.scaleY,b=(l[n]+e.offsetX)*e.scaleX,A=(l[n+1]+e.offsetY)*e.scaleY;if(g>=A)return;const v=h[a],y=h[a+1],w=h[a+2],x=h[r],_=h[r+1],E=h[r+2],C=h[o],S=h[o+1],T=h[o+2],M=Math.round(g),k=Math.round(A);let P,F,D,R,I,L,N,O;for(let t=M;t<=k;t++){if(t<f){const e=t<g?0:(g-t)/(g-f);P=p-(p-m)*e;F=v-(v-x)*e;D=y-(y-_)*e;R=w-(w-E)*e}else{let e;e=t>A?1:f===A?0:(f-t)/(f-A);P=m-(m-b)*e;F=x-(x-C)*e;D=_-(_-S)*e;R=E-(E-T)*e}let e;e=t<g?0:t>A?1:(g-t)/(g-A);I=p-(p-b)*e;L=v-(v-C)*e;N=y-(y-S)*e;O=w-(w-T)*e;const i=Math.round(Math.min(P,I)),s=Math.round(Math.max(P,I));let n=c*t+4*i;for(let t=i;t<=s;t++){e=(P-t)/(P-I);e<0?e=0:e>1&&(e=1);d[n++]=F-(F-L)*e|0;d[n++]=D-(D-N)*e|0;d[n++]=R-(R-O)*e|0;d[n++]=255}}}function drawFigure(t,e,i){const s=e.coords,n=e.colors;let a,r;switch(e.type){case"lattice":const o=e.verticesPerRow,l=Math.floor(s.length/o)-1,h=o-1;for(a=0;a<l;a++){let e=a*o;for(let a=0;a<h;a++,e++){drawTriangle(t,i,s[e],s[e+1],s[e+o],n[e],n[e+1],n[e+o]);drawTriangle(t,i,s[e+o+1],s[e+1],s[e+o],n[e+o+1],n[e+1],n[e+o])}}break;case"triangles":for(a=0,r=s.length;a<r;a+=3)drawTriangle(t,i,s[a],s[a+1],s[a+2],n[a],n[a+1],n[a+2]);break;default:throw new Error("illegal figure")}}class MeshShadingPattern extends BaseShadingPattern{constructor(t){super();this._coords=t[2];this._colors=t[3];this._figures=t[4];this._bounds=t[5];this._bbox=t[7];this._background=t[8];this.matrix=null}_createMeshCanvas(t,e,i){const s=Math.floor(this._bounds[0]),n=Math.floor(this._bounds[1]),a=Math.ceil(this._bounds[2])-s,r=Math.ceil(this._bounds[3])-n,o=Math.min(Math.ceil(Math.abs(a*t[0]*1.1)),3e3),l=Math.min(Math.ceil(Math.abs(r*t[1]*1.1)),3e3),h=a/o,d=r/l,c={coords:this._coords,colors:this._colors,offsetX:-s,offsetY:-n,scaleX:1/h,scaleY:1/d},u=o+4,p=l+4,g=i.getCanvas("mesh",u,p),m=g.context,f=m.createImageData(o,l);if(e){const t=f.data;for(let i=0,s=t.length;i<s;i+=4){t[i]=e[0];t[i+1]=e[1];t[i+2]=e[2];t[i+3]=255}}for(const t of this._figures)drawFigure(f,t,c);m.putImageData(f,2,2);return{canvas:g.canvas,offsetX:s-2*h,offsetY:n-2*d,scaleX:h,scaleY:d}}getPattern(t,e,i,s){applyBoundingBox(t,this._bbox);let n;if(s===xt)n=Util.singularValueDecompose2dScale(getCurrentTransform(t));else{n=Util.singularValueDecompose2dScale(e.baseTransform);if(this.matrix){const t=Util.singularValueDecompose2dScale(this.matrix);n=[n[0]*t[0],n[1]*t[1]]}}const a=this._createMeshCanvas(n,s===xt?null:this._background,e.cachedCanvases);if(s!==xt){t.setTransform(...e.baseTransform);this.matrix&&t.transform(...this.matrix)}t.translate(a.offsetX,a.offsetY);t.scale(a.scaleX,a.scaleY);return t.createPattern(a.canvas,"no-repeat")}}class DummyShadingPattern extends BaseShadingPattern{getPattern(){return"hotpink"}}const _t=1,Et=2;class TilingPattern{static MAX_PATTERN_SIZE=3e3;constructor(t,e,i,s,n){this.operatorList=t[2];this.matrix=t[3];this.bbox=t[4];this.xstep=t[5];this.ystep=t[6];this.paintType=t[7];this.tilingType=t[8];this.color=e;this.ctx=i;this.canvasGraphicsFactory=s;this.baseTransform=n}createPatternCanvas(t){const{bbox:e,operatorList:i,paintType:s,tilingType:n,color:a,canvasGraphicsFactory:r}=this;let{xstep:o,ystep:l}=this;o=Math.abs(o);l=Math.abs(l);info("TilingType: "+n);const h=e[0],d=e[1],c=e[2],u=e[3],p=c-h,g=u-d,m=Util.singularValueDecompose2dScale(this.matrix),f=Util.singularValueDecompose2dScale(this.baseTransform),b=m[0]*f[0],A=m[1]*f[1];let v=p,y=g,w=!1,x=!1;const _=Math.ceil(o*b),E=Math.ceil(l*A);_>=Math.ceil(p*b)?v=o:w=!0;E>=Math.ceil(g*A)?y=l:x=!0;const C=this.getSizeAndScale(v,this.ctx.canvas.width,b),S=this.getSizeAndScale(y,this.ctx.canvas.height,A),T=t.cachedCanvases.getCanvas("pattern",C.size,S.size),M=T.context,k=r.createCanvasGraphics(M);k.groupLevel=t.groupLevel;this.setFillAndStrokeStyleToContext(k,s,a);M.translate(-C.scale*h,-S.scale*d);k.transform(C.scale,0,0,S.scale,0,0);M.save();this.clipBbox(k,h,d,c,u);k.baseTransform=getCurrentTransform(k.ctx);k.executeOperatorList(i);k.endDrawing();M.restore();if(w||x){const e=T.canvas;w&&(v=o);x&&(y=l);const i=this.getSizeAndScale(v,this.ctx.canvas.width,b),s=this.getSizeAndScale(y,this.ctx.canvas.height,A),n=i.size,a=s.size,r=t.cachedCanvases.getCanvas("pattern-workaround",n,a),c=r.context,u=w?Math.floor(p/o):0,m=x?Math.floor(g/l):0;for(let t=0;t<=u;t++)for(let i=0;i<=m;i++)c.drawImage(e,n*t,a*i,n,a,0,0,n,a);return{canvas:r.canvas,scaleX:i.scale,scaleY:s.scale,offsetX:h,offsetY:d}}return{canvas:T.canvas,scaleX:C.scale,scaleY:S.scale,offsetX:h,offsetY:d}}getSizeAndScale(t,e,i){const s=Math.max(TilingPattern.MAX_PATTERN_SIZE,e);let n=Math.ceil(t*i);n>=s?n=s:i=n/t;return{scale:i,size:n}}clipBbox(t,e,i,s,n){const a=s-e,r=n-i;t.ctx.rect(e,i,a,r);t.current.updateRectMinMax(getCurrentTransform(t.ctx),[e,i,s,n]);t.clip();t.endPath()}setFillAndStrokeStyleToContext(t,e,i){const s=t.ctx,n=t.current;switch(e){case _t:const t=this.ctx;s.fillStyle=t.fillStyle;s.strokeStyle=t.strokeStyle;n.fillColor=t.fillStyle;n.strokeColor=t.strokeStyle;break;case Et:const a=Util.makeHexColor(i[0],i[1],i[2]);s.fillStyle=a;s.strokeStyle=a;n.fillColor=a;n.strokeColor=a;break;default:throw new FormatError(`Unsupported paint type: ${e}`)}}getPattern(t,e,i,s){let n=i;if(s!==xt){n=Util.transform(n,e.baseTransform);this.matrix&&(n=Util.transform(n,this.matrix))}const a=this.createPatternCanvas(e);let r=new DOMMatrix(n);r=r.translate(a.offsetX,a.offsetY);r=r.scale(1/a.scaleX,1/a.scaleY);const o=t.createPattern(a.canvas,"repeat");o.setTransform(r);return o}}function convertBlackAndWhiteToRGBA({src:t,srcPos:e=0,dest:i,width:s,height:n,nonBlackColor:a=4294967295,inverseDecode:r=!1}){const o=util_FeatureTest.isLittleEndian?4278190080:255,[l,h]=r?[a,o]:[o,a],d=s>>3,c=7&s,u=t.length;i=new Uint32Array(i.buffer);let p=0;for(let s=0;s<n;s++){for(const s=e+d;e<s;e++){const s=e<u?t[e]:255;i[p++]=128&s?h:l;i[p++]=64&s?h:l;i[p++]=32&s?h:l;i[p++]=16&s?h:l;i[p++]=8&s?h:l;i[p++]=4&s?h:l;i[p++]=2&s?h:l;i[p++]=1&s?h:l}if(0===c)continue;const s=e<u?t[e++]:255;for(let t=0;t<c;t++)i[p++]=s&1<<7-t?h:l}return{srcPos:e,destPos:p}}const Ct=16;class CachedCanvases{constructor(t){this.canvasFactory=t;this.cache=Object.create(null)}getCanvas(t,e,i){let s;if(void 0!==this.cache[t]){s=this.cache[t];this.canvasFactory.reset(s,e,i)}else{s=this.canvasFactory.create(e,i);this.cache[t]=s}return s}delete(t){delete this.cache[t]}clear(){for(const t in this.cache){const e=this.cache[t];this.canvasFactory.destroy(e);delete this.cache[t]}}}function drawImageAtIntegerCoords(t,e,i,s,n,a,r,o,l,h){const[d,c,u,p,g,m]=getCurrentTransform(t);if(0===c&&0===u){const f=r*d+g,b=Math.round(f),A=o*p+m,v=Math.round(A),y=(r+l)*d+g,w=Math.abs(Math.round(y)-b)||1,x=(o+h)*p+m,_=Math.abs(Math.round(x)-v)||1;t.setTransform(Math.sign(d),0,0,Math.sign(p),b,v);t.drawImage(e,i,s,n,a,0,0,w,_);t.setTransform(d,c,u,p,g,m);return[w,_]}if(0===d&&0===p){const f=o*u+g,b=Math.round(f),A=r*c+m,v=Math.round(A),y=(o+h)*u+g,w=Math.abs(Math.round(y)-b)||1,x=(r+l)*c+m,_=Math.abs(Math.round(x)-v)||1;t.setTransform(0,Math.sign(c),Math.sign(u),0,b,v);t.drawImage(e,i,s,n,a,0,0,_,w);t.setTransform(d,c,u,p,g,m);return[_,w]}t.drawImage(e,i,s,n,a,r,o,l,h);return[Math.hypot(d,c)*l,Math.hypot(u,p)*h]}class CanvasExtraState{constructor(t,e){this.alphaIsShape=!1;this.fontSize=0;this.fontSizeScale=1;this.textMatrix=i;this.textMatrixScale=1;this.fontMatrix=s;this.leading=0;this.x=0;this.y=0;this.lineX=0;this.lineY=0;this.charSpacing=0;this.wordSpacing=0;this.textHScale=1;this.textRenderingMode=b;this.textRise=0;this.fillColor="#000000";this.strokeColor="#000000";this.patternFill=!1;this.fillAlpha=1;this.strokeAlpha=1;this.lineWidth=1;this.activeSMask=null;this.transferMaps="none";this.startNewPathAndClipBox([0,0,t,e])}clone(){const t=Object.create(this);t.clipBox=this.clipBox.slice();return t}setCurrentPoint(t,e){this.x=t;this.y=e}updatePathMinMax(t,e,i){[e,i]=Util.applyTransform([e,i],t);this.minX=Math.min(this.minX,e);this.minY=Math.min(this.minY,i);this.maxX=Math.max(this.maxX,e);this.maxY=Math.max(this.maxY,i)}updateRectMinMax(t,e){const i=Util.applyTransform(e,t),s=Util.applyTransform(e.slice(2),t),n=Util.applyTransform([e[0],e[3]],t),a=Util.applyTransform([e[2],e[1]],t);this.minX=Math.min(this.minX,i[0],s[0],n[0],a[0]);this.minY=Math.min(this.minY,i[1],s[1],n[1],a[1]);this.maxX=Math.max(this.maxX,i[0],s[0],n[0],a[0]);this.maxY=Math.max(this.maxY,i[1],s[1],n[1],a[1])}updateScalingPathMinMax(t,e){Util.scaleMinMax(t,e);this.minX=Math.min(this.minX,e[0]);this.minY=Math.min(this.minY,e[1]);this.maxX=Math.max(this.maxX,e[2]);this.maxY=Math.max(this.maxY,e[3])}updateCurvePathMinMax(t,e,i,s,n,a,r,o,l,h){const d=Util.bezierBoundingBox(e,i,s,n,a,r,o,l,h);h||this.updateRectMinMax(t,d)}getPathBoundingBox(t=yt,e=null){const i=[this.minX,this.minY,this.maxX,this.maxY];if(t===wt){e||unreachable("Stroke bounding box must include transform.");const t=Util.singularValueDecompose2dScale(e),s=t[0]*this.lineWidth/2,n=t[1]*this.lineWidth/2;i[0]-=s;i[1]-=n;i[2]+=s;i[3]+=n}return i}updateClipFromPath(){const t=Util.intersect(this.clipBox,this.getPathBoundingBox());this.startNewPathAndClipBox(t||[0,0,0,0])}isEmptyClip(){return this.minX===1/0}startNewPathAndClipBox(t){this.clipBox=t;this.minX=1/0;this.minY=1/0;this.maxX=0;this.maxY=0}getClippedPathBoundingBox(t=yt,e=null){return Util.intersect(this.clipBox,this.getPathBoundingBox(t,e))}}function putBinaryImageData(t,e){if("undefined"!=typeof ImageData&&e instanceof ImageData){t.putImageData(e,0,0);return}const i=e.height,s=e.width,n=i%Ct,a=(i-n)/Ct,r=0===n?a:a+1,o=t.createImageData(s,Ct);let l,h=0;const d=e.data,c=o.data;let u,p,g,m;if(e.kind===_.GRAYSCALE_1BPP){const e=d.byteLength,i=new Uint32Array(c.buffer,0,c.byteLength>>2),m=i.length,f=s+7>>3,b=4294967295,A=util_FeatureTest.isLittleEndian?4278190080:255;for(u=0;u<r;u++){g=u<a?Ct:n;l=0;for(p=0;p<g;p++){const t=e-h;let n=0;const a=t>f?s:8*t-7,r=-8&a;let o=0,c=0;for(;n<r;n+=8){c=d[h++];i[l++]=128&c?b:A;i[l++]=64&c?b:A;i[l++]=32&c?b:A;i[l++]=16&c?b:A;i[l++]=8&c?b:A;i[l++]=4&c?b:A;i[l++]=2&c?b:A;i[l++]=1&c?b:A}for(;n<a;n++){if(0===o){c=d[h++];o=128}i[l++]=c&o?b:A;o>>=1}}for(;l<m;)i[l++]=0;t.putImageData(o,0,u*Ct)}}else if(e.kind===_.RGBA_32BPP){p=0;m=s*Ct*4;for(u=0;u<a;u++){c.set(d.subarray(h,h+m));h+=m;t.putImageData(o,0,p);p+=Ct}if(u<r){m=s*n*4;c.set(d.subarray(h,h+m));t.putImageData(o,0,p)}}else{if(e.kind!==_.RGB_24BPP)throw new Error(`bad image kind: ${e.kind}`);g=Ct;m=s*g;for(u=0;u<r;u++){if(u>=a){g=n;m=s*g}l=0;for(p=m;p--;){c[l++]=d[h++];c[l++]=d[h++];c[l++]=d[h++];c[l++]=255}t.putImageData(o,0,u*Ct)}}}function putBinaryImageMask(t,e){if(e.bitmap){t.drawImage(e.bitmap,0,0);return}const i=e.height,s=e.width,n=i%Ct,a=(i-n)/Ct,r=0===n?a:a+1,o=t.createImageData(s,Ct);let l=0;const h=e.data,d=o.data;for(let e=0;e<r;e++){const i=e<a?Ct:n;({srcPos:l}=convertBlackAndWhiteToRGBA({src:h,srcPos:l,dest:d,width:s,height:i,nonBlackColor:0}));t.putImageData(o,0,e*Ct)}}function copyCtxState(t,e){const i=["strokeStyle","fillStyle","fillRule","globalAlpha","lineWidth","lineCap","lineJoin","miterLimit","globalCompositeOperation","font","filter"];for(const s of i)void 0!==t[s]&&(e[s]=t[s]);if(void 0!==t.setLineDash){e.setLineDash(t.getLineDash());e.lineDashOffset=t.lineDashOffset}}function resetCtxToDefault(t){t.strokeStyle=t.fillStyle="#000000";t.fillRule="nonzero";t.globalAlpha=1;t.lineWidth=1;t.lineCap="butt";t.lineJoin="miter";t.miterLimit=10;t.globalCompositeOperation="source-over";t.font="10px sans-serif";if(void 0!==t.setLineDash){t.setLineDash([]);t.lineDashOffset=0}if(!e){const{filter:e}=t;"none"!==e&&""!==e&&(t.filter="none")}}function getImageSmoothingEnabled(t,e){if(e)return!0;const i=Util.singularValueDecompose2dScale(t);i[0]=Math.fround(i[0]);i[1]=Math.fround(i[1]);const s=Math.fround((globalThis.devicePixelRatio||1)*PixelsPerInch.PDF_TO_CSS_UNITS);return i[0]<=s&&i[1]<=s}const St=["butt","round","square"],Tt=["miter","round","bevel"],Mt={},kt={};class CanvasGraphics{constructor(t,e,i,s,n,{optionalContentConfig:a,markedContentStack:r=null},o,l){this.ctx=t;this.current=new CanvasExtraState(this.ctx.canvas.width,this.ctx.canvas.height);this.stateStack=[];this.pendingClip=null;this.pendingEOFill=!1;this.res=null;this.xobjs=null;this.commonObjs=e;this.objs=i;this.canvasFactory=s;this.filterFactory=n;this.groupStack=[];this.processingType3=null;this.baseTransform=null;this.baseTransformStack=[];this.groupLevel=0;this.smaskStack=[];this.smaskCounter=0;this.tempSMask=null;this.suspendedCtx=null;this.contentVisible=!0;this.markedContentStack=r||[];this.optionalContentConfig=a;this.cachedCanvases=new CachedCanvases(this.canvasFactory);this.cachedPatterns=new Map;this.annotationCanvasMap=o;this.viewportScale=1;this.outputScaleX=1;this.outputScaleY=1;this.pageColors=l;this._cachedScaleForStroking=[-1,0];this._cachedGetSinglePixelWidth=null;this._cachedBitmapsMap=new Map}getObject(t,e=null){return"string"==typeof t?t.startsWith("g_")?this.commonObjs.get(t):this.objs.get(t):e}beginDrawing({transform:t,viewport:e,transparency:i=!1,background:s=null}){const n=this.ctx.canvas.width,a=this.ctx.canvas.height,r=this.ctx.fillStyle;this.ctx.fillStyle=s||"#ffffff";this.ctx.fillRect(0,0,n,a);this.ctx.fillStyle=r;if(i){const t=this.cachedCanvases.getCanvas("transparent",n,a);this.compositeCtx=this.ctx;this.transparentCanvas=t.canvas;this.ctx=t.context;this.ctx.save();this.ctx.transform(...getCurrentTransform(this.compositeCtx))}this.ctx.save();resetCtxToDefault(this.ctx);if(t){this.ctx.transform(...t);this.outputScaleX=t[0];this.outputScaleY=t[0]}this.ctx.transform(...e.transform);this.viewportScale=e.scale;this.baseTransform=getCurrentTransform(this.ctx)}executeOperatorList(t,e,i,s){const n=t.argsArray,a=t.fnArray;let r=e||0;const o=n.length;if(o===r)return r;const l=o-r>10&&"function"==typeof i,h=l?Date.now()+15:0;let d=0;const c=this.commonObjs,u=this.objs;let p;for(;;){if(void 0!==s&&r===s.nextBreakPoint){s.breakIt(r,i);return r}p=a[r];if(p!==K.dependency)this[p].apply(this,n[r]);else for(const t of n[r]){const e=t.startsWith("g_")?c:u;if(!e.has(t)){e.get(t,i);return r}}r++;if(r===o)return r;if(l&&++d>10){if(Date.now()>h){i();return r}d=0}}}#Ie(){for(;this.stateStack.length||this.inSMaskMode;)this.restore();this.current.activeSMask=null;this.ctx.restore();if(this.transparentCanvas){this.ctx=this.compositeCtx;this.ctx.save();this.ctx.setTransform(1,0,0,1,0,0);this.ctx.drawImage(this.transparentCanvas,0,0);this.ctx.restore();this.transparentCanvas=null}}endDrawing(){this.#Ie();this.cachedCanvases.clear();this.cachedPatterns.clear();for(const t of this._cachedBitmapsMap.values()){for(const e of t.values())"undefined"!=typeof HTMLCanvasElement&&e instanceof HTMLCanvasElement&&(e.width=e.height=0);t.clear()}this._cachedBitmapsMap.clear();this.#Le()}#Le(){if(this.pageColors){const t=this.filterFactory.addHCMFilter(this.pageColors.foreground,this.pageColors.background);if("none"!==t){const e=this.ctx.filter;this.ctx.filter=t;this.ctx.drawImage(this.ctx.canvas,0,0);this.ctx.filter=e}}}_scaleImage(t,e){const i=t.width,s=t.height;let n,a,r=Math.max(Math.hypot(e[0],e[1]),1),o=Math.max(Math.hypot(e[2],e[3]),1),l=i,h=s,d="prescale1";for(;r>2&&l>1||o>2&&h>1;){let e=l,i=h;if(r>2&&l>1){e=l>=16384?Math.floor(l/2)-1||1:Math.ceil(l/2);r/=l/e}if(o>2&&h>1){i=h>=16384?Math.floor(h/2)-1||1:Math.ceil(h)/2;o/=h/i}n=this.cachedCanvases.getCanvas(d,e,i);a=n.context;a.clearRect(0,0,e,i);a.drawImage(t,0,0,l,h,0,0,e,i);t=n.canvas;l=e;h=i;d="prescale1"===d?"prescale2":"prescale1"}return{img:t,paintWidth:l,paintHeight:h}}_createMaskCanvas(t){const e=this.ctx,{width:i,height:s}=t,n=this.current.fillColor,a=this.current.patternFill,r=getCurrentTransform(e);let o,l,h,d;if((t.bitmap||t.data)&&t.count>1){const e=t.bitmap||t.data.buffer;l=JSON.stringify(a?r:[r.slice(0,4),n]);o=this._cachedBitmapsMap.get(e);if(!o){o=new Map;this._cachedBitmapsMap.set(e,o)}const i=o.get(l);if(i&&!a){return{canvas:i,offsetX:Math.round(Math.min(r[0],r[2])+r[4]),offsetY:Math.round(Math.min(r[1],r[3])+r[5])}}h=i}if(!h){d=this.cachedCanvases.getCanvas("maskCanvas",i,s);putBinaryImageMask(d.context,t)}let c=Util.transform(r,[1/i,0,0,-1/s,0,0]);c=Util.transform(c,[1,0,0,1,0,-s]);const[u,p,g,m]=Util.getAxialAlignedBoundingBox([0,0,i,s],c),f=Math.round(g-u)||1,b=Math.round(m-p)||1,A=this.cachedCanvases.getCanvas("fillCanvas",f,b),v=A.context,y=u,w=p;v.translate(-y,-w);v.transform(...c);if(!h){h=this._scaleImage(d.canvas,getCurrentTransformInverse(v));h=h.img;o&&a&&o.set(l,h)}v.imageSmoothingEnabled=getImageSmoothingEnabled(getCurrentTransform(v),t.interpolate);drawImageAtIntegerCoords(v,h,0,0,h.width,h.height,0,0,i,s);v.globalCompositeOperation="source-in";const x=Util.transform(getCurrentTransformInverse(v),[1,0,0,1,-y,-w]);v.fillStyle=a?n.getPattern(e,this,x,yt):n;v.fillRect(0,0,i,s);if(o&&!a){this.cachedCanvases.delete("fillCanvas");o.set(l,A.canvas)}return{canvas:A.canvas,offsetX:Math.round(y),offsetY:Math.round(w)}}setLineWidth(t){t!==this.current.lineWidth&&(this._cachedScaleForStroking[0]=-1);this.current.lineWidth=t;this.ctx.lineWidth=t}setLineCap(t){this.ctx.lineCap=St[t]}setLineJoin(t){this.ctx.lineJoin=Tt[t]}setMiterLimit(t){this.ctx.miterLimit=t}setDash(t,e){const i=this.ctx;if(void 0!==i.setLineDash){i.setLineDash(t);i.lineDashOffset=e}}setRenderingIntent(t){}setFlatness(t){}setGState(t){for(const[e,i]of t)switch(e){case"LW":this.setLineWidth(i);break;case"LC":this.setLineCap(i);break;case"LJ":this.setLineJoin(i);break;case"ML":this.setMiterLimit(i);break;case"D":this.setDash(i[0],i[1]);break;case"RI":this.setRenderingIntent(i);break;case"FL":this.setFlatness(i);break;case"Font":this.setFont(i[0],i[1]);break;case"CA":this.current.strokeAlpha=i;break;case"ca":this.current.fillAlpha=i;this.ctx.globalAlpha=i;break;case"BM":this.ctx.globalCompositeOperation=i;break;case"SMask":this.current.activeSMask=i?this.tempSMask:null;this.tempSMask=null;this.checkSMaskState();break;case"TR":this.ctx.filter=this.current.transferMaps=this.filterFactory.addFilter(i)}}get inSMaskMode(){return!!this.suspendedCtx}checkSMaskState(){const t=this.inSMaskMode;this.current.activeSMask&&!t?this.beginSMaskMode():!this.current.activeSMask&&t&&this.endSMaskMode()}beginSMaskMode(){if(this.inSMaskMode)throw new Error("beginSMaskMode called while already in smask mode");const t=this.ctx.canvas.width,e=this.ctx.canvas.height,i="smaskGroupAt"+this.groupLevel,s=this.cachedCanvases.getCanvas(i,t,e);this.suspendedCtx=this.ctx;this.ctx=s.context;const n=this.ctx;n.setTransform(...getCurrentTransform(this.suspendedCtx));copyCtxState(this.suspendedCtx,n);!function mirrorContextOperations(t,e){if(t._removeMirroring)throw new Error("Context is already forwarding operations.");t.__originalSave=t.save;t.__originalRestore=t.restore;t.__originalRotate=t.rotate;t.__originalScale=t.scale;t.__originalTranslate=t.translate;t.__originalTransform=t.transform;t.__originalSetTransform=t.setTransform;t.__originalResetTransform=t.resetTransform;t.__originalClip=t.clip;t.__originalMoveTo=t.moveTo;t.__originalLineTo=t.lineTo;t.__originalBezierCurveTo=t.bezierCurveTo;t.__originalRect=t.rect;t.__originalClosePath=t.closePath;t.__originalBeginPath=t.beginPath;t._removeMirroring=()=>{t.save=t.__originalSave;t.restore=t.__originalRestore;t.rotate=t.__originalRotate;t.scale=t.__originalScale;t.translate=t.__originalTranslate;t.transform=t.__originalTransform;t.setTransform=t.__originalSetTransform;t.resetTransform=t.__originalResetTransform;t.clip=t.__originalClip;t.moveTo=t.__originalMoveTo;t.lineTo=t.__originalLineTo;t.bezierCurveTo=t.__originalBezierCurveTo;t.rect=t.__originalRect;t.closePath=t.__originalClosePath;t.beginPath=t.__originalBeginPath;delete t._removeMirroring};t.save=function ctxSave(){e.save();this.__originalSave()};t.restore=function ctxRestore(){e.restore();this.__originalRestore()};t.translate=function ctxTranslate(t,i){e.translate(t,i);this.__originalTranslate(t,i)};t.scale=function ctxScale(t,i){e.scale(t,i);this.__originalScale(t,i)};t.transform=function ctxTransform(t,i,s,n,a,r){e.transform(t,i,s,n,a,r);this.__originalTransform(t,i,s,n,a,r)};t.setTransform=function ctxSetTransform(t,i,s,n,a,r){e.setTransform(t,i,s,n,a,r);this.__originalSetTransform(t,i,s,n,a,r)};t.resetTransform=function ctxResetTransform(){e.resetTransform();this.__originalResetTransform()};t.rotate=function ctxRotate(t){e.rotate(t);this.__originalRotate(t)};t.clip=function ctxRotate(t){e.clip(t);this.__originalClip(t)};t.moveTo=function(t,i){e.moveTo(t,i);this.__originalMoveTo(t,i)};t.lineTo=function(t,i){e.lineTo(t,i);this.__originalLineTo(t,i)};t.bezierCurveTo=function(t,i,s,n,a,r){e.bezierCurveTo(t,i,s,n,a,r);this.__originalBezierCurveTo(t,i,s,n,a,r)};t.rect=function(t,i,s,n){e.rect(t,i,s,n);this.__originalRect(t,i,s,n)};t.closePath=function(){e.closePath();this.__originalClosePath()};t.beginPath=function(){e.beginPath();this.__originalBeginPath()}}(n,this.suspendedCtx);this.setGState([["BM","source-over"],["ca",1],["CA",1]])}endSMaskMode(){if(!this.inSMaskMode)throw new Error("endSMaskMode called while not in smask mode");this.ctx._removeMirroring();copyCtxState(this.ctx,this.suspendedCtx);this.ctx=this.suspendedCtx;this.suspendedCtx=null}compose(t){if(!this.current.activeSMask)return;if(t){t[0]=Math.floor(t[0]);t[1]=Math.floor(t[1]);t[2]=Math.ceil(t[2]);t[3]=Math.ceil(t[3])}else t=[0,0,this.ctx.canvas.width,this.ctx.canvas.height];const e=this.current.activeSMask,i=this.suspendedCtx;this.composeSMask(i,e,this.ctx,t);this.ctx.save();this.ctx.setTransform(1,0,0,1,0,0);this.ctx.clearRect(0,0,this.ctx.canvas.width,this.ctx.canvas.height);this.ctx.restore()}composeSMask(t,e,i,s){const n=s[0],a=s[1],r=s[2]-n,o=s[3]-a;if(0!==r&&0!==o){this.genericComposeSMask(e.context,i,r,o,e.subtype,e.backdrop,e.transferMap,n,a,e.offsetX,e.offsetY);t.save();t.globalAlpha=1;t.globalCompositeOperation="source-over";t.setTransform(1,0,0,1,0,0);t.drawImage(i.canvas,0,0);t.restore()}}genericComposeSMask(t,e,i,s,n,a,r,o,l,h,d){let c=t.canvas,u=o-h,p=l-d;if(a)if(u<0||p<0||u+i>c.width||p+s>c.height){const t=this.cachedCanvases.getCanvas("maskExtension",i,s),e=t.context;e.drawImage(c,-u,-p);if(a.some((t=>0!==t))){e.globalCompositeOperation="destination-atop";e.fillStyle=Util.makeHexColor(...a);e.fillRect(0,0,i,s);e.globalCompositeOperation="source-over"}c=t.canvas;u=p=0}else if(a.some((t=>0!==t))){t.save();t.globalAlpha=1;t.setTransform(1,0,0,1,0,0);const e=new Path2D;e.rect(u,p,i,s);t.clip(e);t.globalCompositeOperation="destination-atop";t.fillStyle=Util.makeHexColor(...a);t.fillRect(u,p,i,s);t.restore()}e.save();e.globalAlpha=1;e.setTransform(1,0,0,1,0,0);"Alpha"===n&&r?e.filter=this.filterFactory.addAlphaFilter(r):"Luminosity"===n&&(e.filter=this.filterFactory.addLuminosityFilter(r));const g=new Path2D;g.rect(o,l,i,s);e.clip(g);e.globalCompositeOperation="destination-in";e.drawImage(c,u,p,i,s,o,l,i,s);e.restore()}save(){if(this.inSMaskMode){copyCtxState(this.ctx,this.suspendedCtx);this.suspendedCtx.save()}else this.ctx.save();const t=this.current;this.stateStack.push(t);this.current=t.clone()}restore(){0===this.stateStack.length&&this.inSMaskMode&&this.endSMaskMode();if(0!==this.stateStack.length){this.current=this.stateStack.pop();if(this.inSMaskMode){this.suspendedCtx.restore();copyCtxState(this.suspendedCtx,this.ctx)}else this.ctx.restore();this.checkSMaskState();this.pendingClip=null;this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null}}transform(t,e,i,s,n,a){this.ctx.transform(t,e,i,s,n,a);this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null}constructPath(t,e,i){const s=this.ctx,n=this.current;let a,r,o=n.x,l=n.y;const h=getCurrentTransform(s),d=0===h[0]&&0===h[3]||0===h[1]&&0===h[2],c=d?i.slice(0):null;for(let i=0,u=0,p=t.length;i<p;i++)switch(0|t[i]){case K.rectangle:o=e[u++];l=e[u++];const t=e[u++],i=e[u++],p=o+t,g=l+i;s.moveTo(o,l);if(0===t||0===i)s.lineTo(p,g);else{s.lineTo(p,l);s.lineTo(p,g);s.lineTo(o,g)}d||n.updateRectMinMax(h,[o,l,p,g]);s.closePath();break;case K.moveTo:o=e[u++];l=e[u++];s.moveTo(o,l);d||n.updatePathMinMax(h,o,l);break;case K.lineTo:o=e[u++];l=e[u++];s.lineTo(o,l);d||n.updatePathMinMax(h,o,l);break;case K.curveTo:a=o;r=l;o=e[u+4];l=e[u+5];s.bezierCurveTo(e[u],e[u+1],e[u+2],e[u+3],o,l);n.updateCurvePathMinMax(h,a,r,e[u],e[u+1],e[u+2],e[u+3],o,l,c);u+=6;break;case K.curveTo2:a=o;r=l;s.bezierCurveTo(o,l,e[u],e[u+1],e[u+2],e[u+3]);n.updateCurvePathMinMax(h,a,r,o,l,e[u],e[u+1],e[u+2],e[u+3],c);o=e[u+2];l=e[u+3];u+=4;break;case K.curveTo3:a=o;r=l;o=e[u+2];l=e[u+3];s.bezierCurveTo(e[u],e[u+1],o,l,o,l);n.updateCurvePathMinMax(h,a,r,e[u],e[u+1],o,l,o,l,c);u+=4;break;case K.closePath:s.closePath()}d&&n.updateScalingPathMinMax(h,c);n.setCurrentPoint(o,l)}closePath(){this.ctx.closePath()}stroke(t=!0){const e=this.ctx,i=this.current.strokeColor;e.globalAlpha=this.current.strokeAlpha;if(this.contentVisible)if("object"==typeof i&&i?.getPattern){e.save();e.strokeStyle=i.getPattern(e,this,getCurrentTransformInverse(e),wt);this.rescaleAndStroke(!1);e.restore()}else this.rescaleAndStroke(!0);t&&this.consumePath(this.current.getClippedPathBoundingBox());e.globalAlpha=this.current.fillAlpha}closeStroke(){this.closePath();this.stroke()}fill(t=!0){const e=this.ctx,i=this.current.fillColor;let s=!1;if(this.current.patternFill){e.save();e.fillStyle=i.getPattern(e,this,getCurrentTransformInverse(e),yt);s=!0}const n=this.current.getClippedPathBoundingBox();if(this.contentVisible&&null!==n)if(this.pendingEOFill){e.fill("evenodd");this.pendingEOFill=!1}else e.fill();s&&e.restore();t&&this.consumePath(n)}eoFill(){this.pendingEOFill=!0;this.fill()}fillStroke(){this.fill(!1);this.stroke(!1);this.consumePath()}eoFillStroke(){this.pendingEOFill=!0;this.fillStroke()}closeFillStroke(){this.closePath();this.fillStroke()}closeEOFillStroke(){this.pendingEOFill=!0;this.closePath();this.fillStroke()}endPath(){this.consumePath()}clip(){this.pendingClip=Mt}eoClip(){this.pendingClip=kt}beginText(){this.current.textMatrix=i;this.current.textMatrixScale=1;this.current.x=this.current.lineX=0;this.current.y=this.current.lineY=0}endText(){const t=this.pendingTextPaths,e=this.ctx;if(void 0!==t){e.save();e.beginPath();for(const i of t){e.setTransform(...i.transform);e.translate(i.x,i.y);i.addToPath(e,i.fontSize)}e.restore();e.clip();e.beginPath();delete this.pendingTextPaths}else e.beginPath()}setCharSpacing(t){this.current.charSpacing=t}setWordSpacing(t){this.current.wordSpacing=t}setHScale(t){this.current.textHScale=t/100}setLeading(t){this.current.leading=-t}setFont(t,e){const i=this.commonObjs.get(t),n=this.current;if(!i)throw new Error(`Can't find font for ${t}`);n.fontMatrix=i.fontMatrix||s;0!==n.fontMatrix[0]&&0!==n.fontMatrix[3]||warn("Invalid font matrix for font "+t);if(e<0){e=-e;n.fontDirection=-1}else n.fontDirection=1;this.current.font=i;this.current.fontSize=e;if(i.isType3Font)return;const a=i.loadedName||"sans-serif",r=i.systemFontInfo?.css||`"${a}", ${i.fallbackName}`;let o="normal";i.black?o="900":i.bold&&(o="bold");const l=i.italic?"italic":"normal";let h=e;e<16?h=16:e>100&&(h=100);this.current.fontSizeScale=e/h;this.ctx.font=`${l} ${o} ${h}px ${r}`}setTextRenderingMode(t){this.current.textRenderingMode=t}setTextRise(t){this.current.textRise=t}moveText(t,e){this.current.x=this.current.lineX+=t;this.current.y=this.current.lineY+=e}setLeadingMoveText(t,e){this.setLeading(-e);this.moveText(t,e)}setTextMatrix(t,e,i,s,n,a){this.current.textMatrix=[t,e,i,s,n,a];this.current.textMatrixScale=Math.hypot(t,e);this.current.x=this.current.lineX=0;this.current.y=this.current.lineY=0}nextLine(){this.moveText(0,this.current.leading)}paintChar(t,e,i,s){const n=this.ctx,a=this.current,r=a.font,o=a.textRenderingMode,l=a.fontSize/a.fontSizeScale,h=o&w,d=!!(o&x),c=a.patternFill&&!r.missingFile;let u;(r.disableFontFace||d||c)&&(u=r.getPathGenerator(this.commonObjs,t));if(r.disableFontFace||c){n.save();n.translate(e,i);n.beginPath();u(n,l);s&&n.setTransform(...s);h!==b&&h!==v||n.fill();h!==A&&h!==v||n.stroke();n.restore()}else{h!==b&&h!==v||n.fillText(t,e,i);h!==A&&h!==v||n.strokeText(t,e,i)}if(d){(this.pendingTextPaths||=[]).push({transform:getCurrentTransform(n),x:e,y:i,fontSize:l,addToPath:u})}}get isFontSubpixelAAEnabled(){const{context:t}=this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled",10,10);t.scale(1.5,1);t.fillText("I",0,10);const e=t.getImageData(0,0,10,10).data;let i=!1;for(let t=3;t<e.length;t+=4)if(e[t]>0&&e[t]<255){i=!0;break}return shadow(this,"isFontSubpixelAAEnabled",i)}showText(t){const e=this.current,i=e.font;if(i.isType3Font)return this.showType3Text(t);const s=e.fontSize;if(0===s)return;const n=this.ctx,a=e.fontSizeScale,r=e.charSpacing,o=e.wordSpacing,l=e.fontDirection,h=e.textHScale*l,d=t.length,c=i.vertical,u=c?1:-1,p=i.defaultVMetrics,g=s*e.fontMatrix[0],m=e.textRenderingMode===b&&!i.disableFontFace&&!e.patternFill;n.save();n.transform(...e.textMatrix);n.translate(e.x,e.y+e.textRise);l>0?n.scale(h,-1):n.scale(h,1);let f;if(e.patternFill){n.save();const t=e.fillColor.getPattern(n,this,getCurrentTransformInverse(n),yt);f=getCurrentTransform(n);n.restore();n.fillStyle=t}let y=e.lineWidth;const x=e.textMatrixScale;if(0===x||0===y){const t=e.textRenderingMode&w;t!==A&&t!==v||(y=this.getSinglePixelWidth())}else y/=x;if(1!==a){n.scale(a,a);y/=a}n.lineWidth=y;if(i.isInvalidPDFjsFont){const i=[];let s=0;for(const e of t){i.push(e.unicode);s+=e.width}n.fillText(i.join(""),0,0);e.x+=s*g*h;n.restore();this.compose();return}let _,E=0;for(_=0;_<d;++_){const e=t[_];if("number"==typeof e){E+=u*e*s/1e3;continue}let h=!1;const d=(e.isSpace?o:0)+r,b=e.fontChar,A=e.accent;let v,y,w=e.width;if(c){const t=e.vmetric||p,i=-(e.vmetric?t[1]:.5*w)*g,s=t[2]*g;w=t?-t[0]:w;v=i/a;y=(E+s)/a}else{v=E/a;y=0}if(i.remeasure&&w>0){const t=1e3*n.measureText(b).width/s*a;if(w<t&&this.isFontSubpixelAAEnabled){const e=w/t;h=!0;n.save();n.scale(e,1);v/=e}else w!==t&&(v+=(w-t)/2e3*s/a)}if(this.contentVisible&&(e.isInFont||i.missingFile))if(m&&!A)n.fillText(b,v,y);else{this.paintChar(b,v,y,f);if(A){const t=v+s*A.offset.x/a,e=y-s*A.offset.y/a;this.paintChar(A.fontChar,t,e,f)}}E+=c?w*g-d*l:w*g+d*l;h&&n.restore()}c?e.y-=E:e.x+=E*h;n.restore();this.compose()}showType3Text(t){const e=this.ctx,i=this.current,n=i.font,a=i.fontSize,r=i.fontDirection,o=n.vertical?1:-1,l=i.charSpacing,h=i.wordSpacing,d=i.textHScale*r,c=i.fontMatrix||s,u=t.length;let p,g,m,f;if(!(i.textRenderingMode===y)&&0!==a){this._cachedScaleForStroking[0]=-1;this._cachedGetSinglePixelWidth=null;e.save();e.transform(...i.textMatrix);e.translate(i.x,i.y);e.scale(d,r);for(p=0;p<u;++p){g=t[p];if("number"==typeof g){f=o*g*a/1e3;this.ctx.translate(f,0);i.x+=f*d;continue}const s=(g.isSpace?h:0)+l,r=n.charProcOperatorList[g.operatorListId];if(!r){warn(`Type3 character "${g.operatorListId}" is not available.`);continue}if(this.contentVisible){this.processingType3=g;this.save();e.scale(a,a);e.transform(...c);this.executeOperatorList(r);this.restore()}m=Util.applyTransform([g.width,0],c)[0]*a+s;e.translate(m,0);i.x+=m*d}e.restore();this.processingType3=null}}setCharWidth(t,e){}setCharWidthAndBounds(t,e,i,s,n,a){this.ctx.rect(i,s,n-i,a-s);this.ctx.clip();this.endPath()}getColorN_Pattern(t){let e;if("TilingPattern"===t[0]){const i=t[1],s=this.baseTransform||getCurrentTransform(this.ctx),n={createCanvasGraphics:t=>new CanvasGraphics(t,this.commonObjs,this.objs,this.canvasFactory,this.filterFactory,{optionalContentConfig:this.optionalContentConfig,markedContentStack:this.markedContentStack})};e=new TilingPattern(t,i,this.ctx,n,s)}else e=this._getPattern(t[1],t[2]);return e}setStrokeColorN(){this.current.strokeColor=this.getColorN_Pattern(arguments)}setFillColorN(){this.current.fillColor=this.getColorN_Pattern(arguments);this.current.patternFill=!0}setStrokeRGBColor(t,e,i){this.ctx.strokeStyle=this.current.strokeColor=Util.makeHexColor(t,e,i)}setStrokeTransparent(){this.ctx.strokeStyle=this.current.strokeColor="transparent"}setFillRGBColor(t,e,i){this.ctx.fillStyle=this.current.fillColor=Util.makeHexColor(t,e,i);this.current.patternFill=!1}setFillTransparent(){this.ctx.fillStyle=this.current.fillColor="transparent";this.current.patternFill=!1}_getPattern(t,e=null){let i;if(this.cachedPatterns.has(t))i=this.cachedPatterns.get(t);else{i=function getShadingPattern(t){switch(t[0]){case"RadialAxial":return new RadialAxialShadingPattern(t);case"Mesh":return new MeshShadingPattern(t);case"Dummy":return new DummyShadingPattern}throw new Error(`Unknown IR type: ${t[0]}`)}(this.getObject(t));this.cachedPatterns.set(t,i)}e&&(i.matrix=e);return i}shadingFill(t){if(!this.contentVisible)return;const e=this.ctx;this.save();const i=this._getPattern(t);e.fillStyle=i.getPattern(e,this,getCurrentTransformInverse(e),xt);const s=getCurrentTransformInverse(e);if(s){const{width:t,height:i}=e.canvas,[n,a,r,o]=Util.getAxialAlignedBoundingBox([0,0,t,i],s);this.ctx.fillRect(n,a,r-n,o-a)}else this.ctx.fillRect(-1e10,-1e10,2e10,2e10);this.compose(this.current.getClippedPathBoundingBox());this.restore()}beginInlineImage(){unreachable("Should not call beginInlineImage")}beginImageData(){unreachable("Should not call beginImageData")}paintFormXObjectBegin(t,e){if(this.contentVisible){this.save();this.baseTransformStack.push(this.baseTransform);t&&this.transform(...t);this.baseTransform=getCurrentTransform(this.ctx);if(e){const t=e[2]-e[0],i=e[3]-e[1];this.ctx.rect(e[0],e[1],t,i);this.current.updateRectMinMax(getCurrentTransform(this.ctx),e);this.clip();this.endPath()}}}paintFormXObjectEnd(){if(this.contentVisible){this.restore();this.baseTransform=this.baseTransformStack.pop()}}beginGroup(t){if(!this.contentVisible)return;this.save();if(this.inSMaskMode){this.endSMaskMode();this.current.activeSMask=null}const e=this.ctx;t.isolated||info("TODO: Support non-isolated groups.");t.knockout&&warn("Knockout groups not supported.");const i=getCurrentTransform(e);t.matrix&&e.transform(...t.matrix);if(!t.bbox)throw new Error("Bounding box is required.");let s=Util.getAxialAlignedBoundingBox(t.bbox,getCurrentTransform(e));const n=[0,0,e.canvas.width,e.canvas.height];s=Util.intersect(s,n)||[0,0,0,0];const a=Math.floor(s[0]),r=Math.floor(s[1]),o=Math.max(Math.ceil(s[2])-a,1),l=Math.max(Math.ceil(s[3])-r,1);this.current.startNewPathAndClipBox([0,0,o,l]);let h="groupAt"+this.groupLevel;t.smask&&(h+="_smask_"+this.smaskCounter++%2);const d=this.cachedCanvases.getCanvas(h,o,l),c=d.context;c.translate(-a,-r);c.transform(...i);if(t.smask)this.smaskStack.push({canvas:d.canvas,context:c,offsetX:a,offsetY:r,subtype:t.smask.subtype,backdrop:t.smask.backdrop,transferMap:t.smask.transferMap||null,startTransformInverse:null});else{e.setTransform(1,0,0,1,0,0);e.translate(a,r);e.save()}copyCtxState(e,c);this.ctx=c;this.setGState([["BM","source-over"],["ca",1],["CA",1]]);this.groupStack.push(e);this.groupLevel++}endGroup(t){if(!this.contentVisible)return;this.groupLevel--;const e=this.ctx,i=this.groupStack.pop();this.ctx=i;this.ctx.imageSmoothingEnabled=!1;if(t.smask){this.tempSMask=this.smaskStack.pop();this.restore()}else{this.ctx.restore();const t=getCurrentTransform(this.ctx);this.restore();this.ctx.save();this.ctx.setTransform(...t);const i=Util.getAxialAlignedBoundingBox([0,0,e.canvas.width,e.canvas.height],t);this.ctx.drawImage(e.canvas,0,0);this.ctx.restore();this.compose(i)}}beginAnnotation(t,e,i,s,n){this.#Ie();resetCtxToDefault(this.ctx);this.ctx.save();this.save();this.baseTransform&&this.ctx.setTransform(...this.baseTransform);if(e){const s=e[2]-e[0],a=e[3]-e[1];if(n&&this.annotationCanvasMap){(i=i.slice())[4]-=e[0];i[5]-=e[1];(e=e.slice())[0]=e[1]=0;e[2]=s;e[3]=a;const[n,r]=Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx)),{viewportScale:o}=this,l=Math.ceil(s*this.outputScaleX*o),h=Math.ceil(a*this.outputScaleY*o);this.annotationCanvas=this.canvasFactory.create(l,h);const{canvas:d,context:c}=this.annotationCanvas;this.annotationCanvasMap.set(t,d);this.annotationCanvas.savedCtx=this.ctx;this.ctx=c;this.ctx.save();this.ctx.setTransform(n,0,0,-r,0,a*r);resetCtxToDefault(this.ctx)}else{resetCtxToDefault(this.ctx);this.endPath();this.ctx.rect(e[0],e[1],s,a);this.ctx.clip();this.ctx.beginPath()}}this.current=new CanvasExtraState(this.ctx.canvas.width,this.ctx.canvas.height);this.transform(...i);this.transform(...s)}endAnnotation(){if(this.annotationCanvas){this.ctx.restore();this.#Le();this.ctx=this.annotationCanvas.savedCtx;delete this.annotationCanvas.savedCtx;delete this.annotationCanvas}}paintImageMaskXObject(t){if(!this.contentVisible)return;const e=t.count;(t=this.getObject(t.data,t)).count=e;const i=this.ctx,s=this.processingType3;if(s){void 0===s.compiled&&(s.compiled=function compileType3Glyph(t){const{width:e,height:i}=t;if(e>1e3||i>1e3)return null;const s=new Uint8Array([0,2,4,0,1,0,5,4,8,10,0,8,0,2,1,0]),n=e+1;let a,r,o,l=new Uint8Array(n*(i+1));const h=e+7&-8;let d=new Uint8Array(h*i),c=0;for(const e of t.data){let t=128;for(;t>0;){d[c++]=e&t?0:255;t>>=1}}let u=0;c=0;if(0!==d[c]){l[0]=1;++u}for(r=1;r<e;r++){if(d[c]!==d[c+1]){l[r]=d[c]?2:1;++u}c++}if(0!==d[c]){l[r]=2;++u}for(a=1;a<i;a++){c=a*h;o=a*n;if(d[c-h]!==d[c]){l[o]=d[c]?1:8;++u}let t=(d[c]?4:0)+(d[c-h]?8:0);for(r=1;r<e;r++){t=(t>>2)+(d[c+1]?4:0)+(d[c-h+1]?8:0);if(s[t]){l[o+r]=s[t];++u}c++}if(d[c-h]!==d[c]){l[o+r]=d[c]?2:4;++u}if(u>1e3)return null}c=h*(i-1);o=a*n;if(0!==d[c]){l[o]=8;++u}for(r=1;r<e;r++){if(d[c]!==d[c+1]){l[o+r]=d[c]?4:8;++u}c++}if(0!==d[c]){l[o+r]=4;++u}if(u>1e3)return null;const p=new Int32Array([0,n,-1,0,-n,0,0,0,1]),g=new Path2D;for(a=0;u&&a<=i;a++){let t=a*n;const i=t+e;for(;t<i&&!l[t];)t++;if(t===i)continue;g.moveTo(t%n,a);const s=t;let r=l[t];do{const e=p[r];do{t+=e}while(!l[t]);const i=l[t];if(5!==i&&10!==i){r=i;l[t]=0}else{r=i&51*r>>4;l[t]&=r>>2|r<<2}g.lineTo(t%n,t/n|0);l[t]||--u}while(s!==t);--a}d=null;l=null;return function(t){t.save();t.scale(1/e,-1/i);t.translate(0,-i);t.fill(g);t.beginPath();t.restore()}}(t));if(s.compiled){s.compiled(i);return}}const n=this._createMaskCanvas(t),a=n.canvas;i.save();i.setTransform(1,0,0,1,0,0);i.drawImage(a,n.offsetX,n.offsetY);i.restore();this.compose()}paintImageMaskXObjectRepeat(t,e,i=0,s=0,n,a){if(!this.contentVisible)return;t=this.getObject(t.data,t);const r=this.ctx;r.save();const o=getCurrentTransform(r);r.transform(e,i,s,n,0,0);const l=this._createMaskCanvas(t);r.setTransform(1,0,0,1,l.offsetX-o[4],l.offsetY-o[5]);for(let t=0,h=a.length;t<h;t+=2){const h=Util.transform(o,[e,i,s,n,a[t],a[t+1]]),[d,c]=Util.applyTransform([0,0],h);r.drawImage(l.canvas,d,c)}r.restore();this.compose()}paintImageMaskXObjectGroup(t){if(!this.contentVisible)return;const e=this.ctx,i=this.current.fillColor,s=this.current.patternFill;for(const n of t){const{data:t,width:a,height:r,transform:o}=n,l=this.cachedCanvases.getCanvas("maskCanvas",a,r),h=l.context;h.save();putBinaryImageMask(h,this.getObject(t,n));h.globalCompositeOperation="source-in";h.fillStyle=s?i.getPattern(h,this,getCurrentTransformInverse(e),yt):i;h.fillRect(0,0,a,r);h.restore();e.save();e.transform(...o);e.scale(1,-1);drawImageAtIntegerCoords(e,l.canvas,0,0,a,r,0,-1,1,1);e.restore()}this.compose()}paintImageXObject(t){if(!this.contentVisible)return;const e=this.getObject(t);e?this.paintInlineImageXObject(e):warn("Dependent image isn't ready yet")}paintImageXObjectRepeat(t,e,i,s){if(!this.contentVisible)return;const n=this.getObject(t);if(!n){warn("Dependent image isn't ready yet");return}const a=n.width,r=n.height,o=[];for(let t=0,n=s.length;t<n;t+=2)o.push({transform:[e,0,0,i,s[t],s[t+1]],x:0,y:0,w:a,h:r});this.paintInlineImageXObjectGroup(n,o)}applyTransferMapsToCanvas(t){if("none"!==this.current.transferMaps){t.filter=this.current.transferMaps;t.drawImage(t.canvas,0,0);t.filter="none"}return t.canvas}applyTransferMapsToBitmap(t){if("none"===this.current.transferMaps)return t.bitmap;const{bitmap:e,width:i,height:s}=t,n=this.cachedCanvases.getCanvas("inlineImage",i,s),a=n.context;a.filter=this.current.transferMaps;a.drawImage(e,0,0);a.filter="none";return n.canvas}paintInlineImageXObject(t){if(!this.contentVisible)return;const i=t.width,s=t.height,n=this.ctx;this.save();if(!e){const{filter:t}=n;"none"!==t&&""!==t&&(n.filter="none")}n.scale(1/i,-1/s);let a;if(t.bitmap)a=this.applyTransferMapsToBitmap(t);else if("function"==typeof HTMLElement&&t instanceof HTMLElement||!t.data)a=t;else{const e=this.cachedCanvases.getCanvas("inlineImage",i,s).context;putBinaryImageData(e,t);a=this.applyTransferMapsToCanvas(e)}const r=this._scaleImage(a,getCurrentTransformInverse(n));n.imageSmoothingEnabled=getImageSmoothingEnabled(getCurrentTransform(n),t.interpolate);drawImageAtIntegerCoords(n,r.img,0,0,r.paintWidth,r.paintHeight,0,-s,i,s);this.compose();this.restore()}paintInlineImageXObjectGroup(t,e){if(!this.contentVisible)return;const i=this.ctx;let s;if(t.bitmap)s=t.bitmap;else{const e=t.width,i=t.height,n=this.cachedCanvases.getCanvas("inlineImage",e,i).context;putBinaryImageData(n,t);s=this.applyTransferMapsToCanvas(n)}for(const t of e){i.save();i.transform(...t.transform);i.scale(1,-1);drawImageAtIntegerCoords(i,s,t.x,t.y,t.w,t.h,0,-1,1,1);i.restore()}this.compose()}paintSolidColorImageMask(){if(this.contentVisible){this.ctx.fillRect(0,0,1,1);this.compose()}}markPoint(t){}markPointProps(t,e){}beginMarkedContent(t){this.markedContentStack.push({visible:!0})}beginMarkedContentProps(t,e){"OC"===t?this.markedContentStack.push({visible:this.optionalContentConfig.isVisible(e)}):this.markedContentStack.push({visible:!0});this.contentVisible=this.isContentVisible()}endMarkedContent(){this.markedContentStack.pop();this.contentVisible=this.isContentVisible()}beginCompat(){}endCompat(){}consumePath(t){const e=this.current.isEmptyClip();this.pendingClip&&this.current.updateClipFromPath();this.pendingClip||this.compose(t);const i=this.ctx;if(this.pendingClip){e||(this.pendingClip===kt?i.clip("evenodd"):i.clip());this.pendingClip=null}this.current.startNewPathAndClipBox(this.current.clipBox);i.beginPath()}getSinglePixelWidth(){if(!this._cachedGetSinglePixelWidth){const t=getCurrentTransform(this.ctx);if(0===t[1]&&0===t[2])this._cachedGetSinglePixelWidth=1/Math.min(Math.abs(t[0]),Math.abs(t[3]));else{const e=Math.abs(t[0]*t[3]-t[2]*t[1]),i=Math.hypot(t[0],t[2]),s=Math.hypot(t[1],t[3]);this._cachedGetSinglePixelWidth=Math.max(i,s)/e}}return this._cachedGetSinglePixelWidth}getScaleForStroking(){if(-1===this._cachedScaleForStroking[0]){const{lineWidth:t}=this.current,{a:e,b:i,c:s,d:n}=this.ctx.getTransform();let a,r;if(0===i&&0===s){const i=Math.abs(e),s=Math.abs(n);if(i===s)if(0===t)a=r=1/i;else{const e=i*t;a=r=e<1?1/e:1}else if(0===t){a=1/i;r=1/s}else{const e=i*t,n=s*t;a=e<1?1/e:1;r=n<1?1/n:1}}else{const o=Math.abs(e*n-i*s),l=Math.hypot(e,i),h=Math.hypot(s,n);if(0===t){a=h/o;r=l/o}else{const e=t*o;a=h>e?h/e:1;r=l>e?l/e:1}}this._cachedScaleForStroking[0]=a;this._cachedScaleForStroking[1]=r}return this._cachedScaleForStroking}rescaleAndStroke(t){const{ctx:e}=this,{lineWidth:i}=this.current,[s,n]=this.getScaleForStroking();e.lineWidth=i||1;if(1===s&&1===n){e.stroke();return}const a=e.getLineDash();t&&e.save();e.scale(s,n);if(a.length>0){const t=Math.max(s,n);e.setLineDash(a.map((e=>e/t)));e.lineDashOffset/=t}e.stroke();t&&e.restore()}isContentVisible(){for(let t=this.markedContentStack.length-1;t>=0;t--)if(!this.markedContentStack[t].visible)return!1;return!0}}for(const t in K)void 0!==CanvasGraphics.prototype[t]&&(CanvasGraphics.prototype[K[t]]=CanvasGraphics.prototype[t]);class GlobalWorkerOptions{static#Ne=null;static#Oe="";static get workerPort(){return this.#Ne}static set workerPort(t){if(!("undefined"!=typeof Worker&&t instanceof Worker)&&null!==t)throw new Error("Invalid `workerPort` type.");this.#Ne=t}static get workerSrc(){return this.#Oe}static set workerSrc(t){if("string"!=typeof t)throw new Error("Invalid `workerSrc` type.");this.#Oe=t}}const Pt=1,Ft=2,Dt=1,Rt=2,It=3,Lt=4,Nt=5,Ot=6,Bt=7,Ht=8;function wrapReason(t){t instanceof Error||"object"==typeof t&&null!==t||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(t.name){case"AbortException":return new AbortException(t.message);case"MissingPDFException":return new MissingPDFException(t.message);case"PasswordException":return new PasswordException(t.message,t.code);case"UnexpectedResponseException":return new UnexpectedResponseException(t.message,t.status);case"UnknownErrorException":return new UnknownErrorException(t.message,t.details);default:return new UnknownErrorException(t.message,t.toString())}}class MessageHandler{constructor(t,e,i){this.sourceName=t;this.targetName=e;this.comObj=i;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);this._onComObjOnMessage=t=>{const e=t.data;if(e.targetName!==this.sourceName)return;if(e.stream){this.#Be(e);return}if(e.callback){const t=e.callbackId,i=this.callbackCapabilities[t];if(!i)throw new Error(`Cannot resolve callback ${t}`);delete this.callbackCapabilities[t];if(e.callback===Pt)i.resolve(e.data);else{if(e.callback!==Ft)throw new Error("Unexpected callback case");i.reject(wrapReason(e.reason))}return}const s=this.actionHandler[e.action];if(!s)throw new Error(`Unknown action from worker: ${e.action}`);if(e.callbackId){const t=this.sourceName,n=e.sourceName;new Promise((function(t){t(s(e.data))})).then((function(s){i.postMessage({sourceName:t,targetName:n,callback:Pt,callbackId:e.callbackId,data:s})}),(function(s){i.postMessage({sourceName:t,targetName:n,callback:Ft,callbackId:e.callbackId,reason:wrapReason(s)})}))}else e.streamId?this.#He(e):s(e.data)};i.addEventListener("message",this._onComObjOnMessage)}on(t,e){const i=this.actionHandler;if(i[t])throw new Error(`There is already an actionName called "${t}"`);i[t]=e}send(t,e,i){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:t,data:e},i)}sendWithPromise(t,e,i){const s=this.callbackId++,n=Promise.withResolvers();this.callbackCapabilities[s]=n;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:t,callbackId:s,data:e},i)}catch(t){n.reject(t)}return n.promise}sendWithStream(t,e,i,s){const n=this.streamId++,a=this.sourceName,r=this.targetName,o=this.comObj;return new ReadableStream({start:i=>{const l=Promise.withResolvers();this.streamControllers[n]={controller:i,startCall:l,pullCall:null,cancelCall:null,isClosed:!1};o.postMessage({sourceName:a,targetName:r,action:t,streamId:n,data:e,desiredSize:i.desiredSize},s);return l.promise},pull:t=>{const e=Promise.withResolvers();this.streamControllers[n].pullCall=e;o.postMessage({sourceName:a,targetName:r,stream:Ot,streamId:n,desiredSize:t.desiredSize});return e.promise},cancel:t=>{assert(t instanceof Error,"cancel must have a valid reason");const e=Promise.withResolvers();this.streamControllers[n].cancelCall=e;this.streamControllers[n].isClosed=!0;o.postMessage({sourceName:a,targetName:r,stream:Dt,streamId:n,reason:wrapReason(t)});return e.promise}},i)}#He(t){const e=t.streamId,i=this.sourceName,s=t.sourceName,n=this.comObj,a=this,r=this.actionHandler[t.action],o={enqueue(t,a=1,r){if(this.isCancelled)return;const o=this.desiredSize;this.desiredSize-=a;if(o>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}n.postMessage({sourceName:i,targetName:s,stream:Lt,streamId:e,chunk:t},r)},close(){if(!this.isCancelled){this.isCancelled=!0;n.postMessage({sourceName:i,targetName:s,stream:It,streamId:e});delete a.streamSinks[e]}},error(t){assert(t instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;n.postMessage({sourceName:i,targetName:s,stream:Nt,streamId:e,reason:wrapReason(t)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:t.desiredSize,ready:null};o.sinkCapability.resolve();o.ready=o.sinkCapability.promise;this.streamSinks[e]=o;new Promise((function(e){e(r(t.data,o))})).then((function(){n.postMessage({sourceName:i,targetName:s,stream:Ht,streamId:e,success:!0})}),(function(t){n.postMessage({sourceName:i,targetName:s,stream:Ht,streamId:e,reason:wrapReason(t)})}))}#Be(t){const e=t.streamId,i=this.sourceName,s=t.sourceName,n=this.comObj,a=this.streamControllers[e],r=this.streamSinks[e];switch(t.stream){case Ht:t.success?a.startCall.resolve():a.startCall.reject(wrapReason(t.reason));break;case Bt:t.success?a.pullCall.resolve():a.pullCall.reject(wrapReason(t.reason));break;case Ot:if(!r){n.postMessage({sourceName:i,targetName:s,stream:Bt,streamId:e,success:!0});break}r.desiredSize<=0&&t.desiredSize>0&&r.sinkCapability.resolve();r.desiredSize=t.desiredSize;new Promise((function(t){t(r.onPull?.())})).then((function(){n.postMessage({sourceName:i,targetName:s,stream:Bt,streamId:e,success:!0})}),(function(t){n.postMessage({sourceName:i,targetName:s,stream:Bt,streamId:e,reason:wrapReason(t)})}));break;case Lt:assert(a,"enqueue should have stream controller");if(a.isClosed)break;a.controller.enqueue(t.chunk);break;case It:assert(a,"close should have stream controller");if(a.isClosed)break;a.isClosed=!0;a.controller.close();this.#ze(a,e);break;case Nt:assert(a,"error should have stream controller");a.controller.error(wrapReason(t.reason));this.#ze(a,e);break;case Rt:t.success?a.cancelCall.resolve():a.cancelCall.reject(wrapReason(t.reason));this.#ze(a,e);break;case Dt:if(!r)break;new Promise((function(e){e(r.onCancel?.(wrapReason(t.reason)))})).then((function(){n.postMessage({sourceName:i,targetName:s,stream:Rt,streamId:e,success:!0})}),(function(t){n.postMessage({sourceName:i,targetName:s,stream:Rt,streamId:e,reason:wrapReason(t)})}));r.sinkCapability.reject(wrapReason(t.reason));r.isCancelled=!0;delete this.streamSinks[e];break;default:throw new Error("Unexpected stream case")}}async#ze(t,e){await Promise.allSettled([t.startCall?.promise,t.pullCall?.promise,t.cancelCall?.promise]);delete this.streamControllers[e]}destroy(){this.comObj.removeEventListener("message",this._onComObjOnMessage)}}class Metadata{#Ue;#je;constructor({parsedData:t,rawData:e}){this.#Ue=t;this.#je=e}getRaw(){return this.#je}get(t){return this.#Ue.get(t)??null}getAll(){return objectFromMap(this.#Ue)}has(t){return this.#Ue.has(t)}}const zt=Symbol("INTERNAL");class OptionalContentGroup{#We=!1;#Ge=!1;#$e=!1;#Ve=!0;constructor(t,{name:e,intent:i,usage:s}){this.#We=!!(t&r);this.#Ge=!!(t&o);this.name=e;this.intent=i;this.usage=s}get visible(){if(this.#$e)return this.#Ve;if(!this.#Ve)return!1;const{print:t,view:e}=this.usage;return this.#We?"OFF"!==e?.viewState:!this.#Ge||"OFF"!==t?.printState}_setVisible(t,e,i=!1){t!==zt&&unreachable("Internal method `_setVisible` called.");this.#$e=i;this.#Ve=e}}class OptionalContentConfig{#qe=null;#Xe=new Map;#Ke=null;#Ye=null;constructor(t,e=r){this.renderingIntent=e;this.name=null;this.creator=null;if(null!==t){this.name=t.name;this.creator=t.creator;this.#Ye=t.order;for(const i of t.groups)this.#Xe.set(i.id,new OptionalContentGroup(e,i));if("OFF"===t.baseState)for(const t of this.#Xe.values())t._setVisible(zt,!1);for(const e of t.on)this.#Xe.get(e)._setVisible(zt,!0);for(const e of t.off)this.#Xe.get(e)._setVisible(zt,!1);this.#Ke=this.getHash()}}#Qe(t){const e=t.length;if(e<2)return!0;const i=t[0];for(let s=1;s<e;s++){const e=t[s];let n;if(Array.isArray(e))n=this.#Qe(e);else{if(!this.#Xe.has(e)){warn(`Optional content group not found: ${e}`);return!0}n=this.#Xe.get(e).visible}switch(i){case"And":if(!n)return!1;break;case"Or":if(n)return!0;break;case"Not":return!n;default:return!0}}return"And"===i}isVisible(t){if(0===this.#Xe.size)return!0;if(!t){info("Optional content group not defined.");return!0}if("OCG"===t.type){if(!this.#Xe.has(t.id)){warn(`Optional content group not found: ${t.id}`);return!0}return this.#Xe.get(t.id).visible}if("OCMD"===t.type){if(t.expression)return this.#Qe(t.expression);if(!t.policy||"AnyOn"===t.policy){for(const e of t.ids){if(!this.#Xe.has(e)){warn(`Optional content group not found: ${e}`);return!0}if(this.#Xe.get(e).visible)return!0}return!1}if("AllOn"===t.policy){for(const e of t.ids){if(!this.#Xe.has(e)){warn(`Optional content group not found: ${e}`);return!0}if(!this.#Xe.get(e).visible)return!1}return!0}if("AnyOff"===t.policy){for(const e of t.ids){if(!this.#Xe.has(e)){warn(`Optional content group not found: ${e}`);return!0}if(!this.#Xe.get(e).visible)return!0}return!1}if("AllOff"===t.policy){for(const e of t.ids){if(!this.#Xe.has(e)){warn(`Optional content group not found: ${e}`);return!0}if(this.#Xe.get(e).visible)return!1}return!0}warn(`Unknown optional content policy ${t.policy}.`);return!0}warn(`Unknown group type ${t.type}.`);return!0}setVisibility(t,e=!0){const i=this.#Xe.get(t);if(i){i._setVisible(zt,!!e,!0);this.#qe=null}else warn(`Optional content group not found: ${t}`)}setOCGState({state:t,preserveRB:e}){let i;for(const e of t){switch(e){case"ON":case"OFF":case"Toggle":i=e;continue}const t=this.#Xe.get(e);if(t)switch(i){case"ON":t._setVisible(zt,!0);break;case"OFF":t._setVisible(zt,!1);break;case"Toggle":t._setVisible(zt,!t.visible)}}this.#qe=null}get hasInitialVisibility(){return null===this.#Ke||this.getHash()===this.#Ke}getOrder(){return this.#Xe.size?this.#Ye?this.#Ye.slice():[...this.#Xe.keys()]:null}getGroups(){return this.#Xe.size>0?objectFromMap(this.#Xe):null}getGroup(t){return this.#Xe.get(t)||null}getHash(){if(null!==this.#qe)return this.#qe;const t=new MurmurHash3_64;for(const[e,i]of this.#Xe)t.update(`${e}:${i.visible}`);return this.#qe=t.hexdigest()}}class PDFDataTransportStream{constructor(t,{disableRange:e=!1,disableStream:i=!1}){assert(t,'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.');const{length:s,initialData:n,progressiveDone:a,contentDispositionFilename:r}=t;this._queuedChunks=[];this._progressiveDone=a;this._contentDispositionFilename=r;if(n?.length>0){const t=n instanceof Uint8Array&&n.byteLength===n.buffer.byteLength?n.buffer:new Uint8Array(n).buffer;this._queuedChunks.push(t)}this._pdfDataRangeTransport=t;this._isStreamingSupported=!i;this._isRangeSupported=!e;this._contentLength=s;this._fullRequestReader=null;this._rangeReaders=[];t.addRangeListener(((t,e)=>{this._onReceiveData({begin:t,chunk:e})}));t.addProgressListener(((t,e)=>{this._onProgress({loaded:t,total:e})}));t.addProgressiveReadListener((t=>{this._onReceiveData({chunk:t})}));t.addProgressiveDoneListener((()=>{this._onProgressiveDone()}));t.transportReady()}_onReceiveData({begin:t,chunk:e}){const i=e instanceof Uint8Array&&e.byteLength===e.buffer.byteLength?e.buffer:new Uint8Array(e).buffer;if(void 0===t)this._fullRequestReader?this._fullRequestReader._enqueue(i):this._queuedChunks.push(i);else{assert(this._rangeReaders.some((function(e){if(e._begin!==t)return!1;e._enqueue(i);return!0})),"_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found.")}}get _progressiveDataLength(){return this._fullRequestReader?._loaded??0}_onProgress(t){void 0===t.total?this._rangeReaders[0]?.onProgress?.({loaded:t.loaded}):this._fullRequestReader?.onProgress?.({loaded:t.loaded,total:t.total})}_onProgressiveDone(){this._fullRequestReader?.progressiveDone();this._progressiveDone=!0}_removeRangeReader(t){const e=this._rangeReaders.indexOf(t);e>=0&&this._rangeReaders.splice(e,1)}getFullReader(){assert(!this._fullRequestReader,"PDFDataTransportStream.getFullReader can only be called once.");const t=this._queuedChunks;this._queuedChunks=null;return new PDFDataTransportStreamReader(this,t,this._progressiveDone,this._contentDispositionFilename)}getRangeReader(t,e){if(e<=this._progressiveDataLength)return null;const i=new PDFDataTransportStreamRangeReader(this,t,e);this._pdfDataRangeTransport.requestDataRange(t,e);this._rangeReaders.push(i);return i}cancelAllRequests(t){this._fullRequestReader?.cancel(t);for(const e of this._rangeReaders.slice(0))e.cancel(t);this._pdfDataRangeTransport.abort()}}class PDFDataTransportStreamReader{constructor(t,e,i=!1,s=null){this._stream=t;this._done=i||!1;this._filename=isPdfFile(s)?s:null;this._queuedChunks=e||[];this._loaded=0;for(const t of this._queuedChunks)this._loaded+=t.byteLength;this._requests=[];this._headersReady=Promise.resolve();t._fullRequestReader=this;this.onProgress=null}_enqueue(t){if(!this._done){if(this._requests.length>0){this._requests.shift().resolve({value:t,done:!1})}else this._queuedChunks.push(t);this._loaded+=t.byteLength}}get headersReady(){return this._headersReady}get filename(){return this._filename}get isRangeSupported(){return this._stream._isRangeSupported}get isStreamingSupported(){return this._stream._isStreamingSupported}get contentLength(){return this._stream._contentLength}async read(){if(this._queuedChunks.length>0){return{value:this._queuedChunks.shift(),done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0}progressiveDone(){this._done||(this._done=!0)}}class PDFDataTransportStreamRangeReader{constructor(t,e,i){this._stream=t;this._begin=e;this._end=i;this._queuedChunk=null;this._requests=[];this._done=!1;this.onProgress=null}_enqueue(t){if(!this._done){if(0===this._requests.length)this._queuedChunk=t;else{this._requests.shift().resolve({value:t,done:!1});for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0}this._done=!0;this._stream._removeRangeReader(this)}}get isStreamingSupported(){return!1}async read(){if(this._queuedChunk){const t=this._queuedChunk;this._queuedChunk=null;return{value:t,done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0;this._stream._removeRangeReader(this)}}function createHeaders(t,e){const i=new Headers;if(!t||!e||"object"!=typeof e)return i;for(const t in e){const s=e[t];void 0!==s&&i.append(t,s)}return i}function validateRangeRequestCapabilities({responseHeaders:t,isHttp:e,rangeChunkSize:i,disableRange:s}){const n={allowRangeRequests:!1,suggestedLength:void 0},a=parseInt(t.get("Content-Length"),10);if(!Number.isInteger(a))return n;n.suggestedLength=a;if(a<=2*i)return n;if(s||!e)return n;if("bytes"!==t.get("Accept-Ranges"))return n;if("identity"!==(t.get("Content-Encoding")||"identity"))return n;n.allowRangeRequests=!0;return n}function extractFilenameFromHeader(t){const e=t.get("Content-Disposition");if(e){let t=function getFilenameFromContentDispositionHeader(t){let e=!0,i=toParamRegExp("filename\\*","i").exec(t);if(i){i=i[1];let t=rfc2616unquote(i);t=unescape(t);t=rfc5987decode(t);t=rfc2047decode(t);return fixupEncoding(t)}i=function rfc2231getparam(t){const e=[];let i;const s=toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)","ig");for(;null!==(i=s.exec(t));){let[,t,s,n]=i;t=parseInt(t,10);if(t in e){if(0===t)break}else e[t]=[s,n]}const n=[];for(let t=0;t<e.length&&t in e;++t){let[i,s]=e[t];s=rfc2616unquote(s);if(i){s=unescape(s);0===t&&(s=rfc5987decode(s))}n.push(s)}return n.join("")}(t);if(i)return fixupEncoding(rfc2047decode(i));i=toParamRegExp("filename","i").exec(t);if(i){i=i[1];let t=rfc2616unquote(i);t=rfc2047decode(t);return fixupEncoding(t)}function toParamRegExp(t,e){return new RegExp("(?:^|;)\\s*"+t+'\\s*=\\s*([^";\\s][^;\\s]*|"(?:[^"\\\\]|\\\\"?)+"?)',e)}function textdecode(t,i){if(t){if(!/^[\x00-\xFF]+$/.test(i))return i;try{const s=new TextDecoder(t,{fatal:!0}),n=stringToBytes(i);i=s.decode(n);e=!1}catch{}}return i}function fixupEncoding(t){if(e&&/[\x80-\xff]/.test(t)){t=textdecode("utf-8",t);e&&(t=textdecode("iso-8859-1",t))}return t}function rfc2616unquote(t){if(t.startsWith('"')){const e=t.slice(1).split('\\"');for(let t=0;t<e.length;++t){const i=e[t].indexOf('"');if(-1!==i){e[t]=e[t].slice(0,i);e.length=t+1}e[t]=e[t].replaceAll(/\\(.)/g,"$1")}t=e.join('"')}return t}function rfc5987decode(t){const e=t.indexOf("'");return-1===e?t:textdecode(t.slice(0,e),t.slice(e+1).replace(/^[^']*'/,""))}function rfc2047decode(t){return!t.startsWith("=?")||/[\x00-\x19\x80-\xff]/.test(t)?t:t.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g,(function(t,e,i,s){if("q"===i||"Q"===i)return textdecode(e,s=(s=s.replaceAll("_"," ")).replaceAll(/=([0-9a-fA-F]{2})/g,(function(t,e){return String.fromCharCode(parseInt(e,16))})));try{s=atob(s)}catch{}return textdecode(e,s)}))}return""}(e);if(t.includes("%"))try{t=decodeURIComponent(t)}catch{}if(isPdfFile(t))return t}return null}function createResponseStatusError(t,e){return 404===t||0===t&&e.startsWith("file:")?new MissingPDFException('Missing PDF "'+e+'".'):new UnexpectedResponseException(`Unexpected server response (${t}) while retrieving PDF "${e}".`,t)}function validateResponseStatus(t){return 200===t||206===t}function createFetchOptions(t,e,i){return{method:"GET",headers:t,signal:i.signal,mode:"cors",credentials:e?"include":"same-origin",redirect:"follow"}}function getArrayBuffer(t){if(t instanceof Uint8Array)return t.buffer;if(t instanceof ArrayBuffer)return t;warn(`getArrayBuffer - unexpected data format: ${t}`);return new Uint8Array(t).buffer}class PDFFetchStream{constructor(t){this.source=t;this.isHttp=/^https?:/i.test(t.url);this.headers=createHeaders(this.isHttp,t.httpHeaders);this._fullRequestReader=null;this._rangeRequestReaders=[]}get _progressiveDataLength(){return this._fullRequestReader?._loaded??0}getFullReader(){assert(!this._fullRequestReader,"PDFFetchStream.getFullReader can only be called once.");this._fullRequestReader=new PDFFetchStreamReader(this);return this._fullRequestReader}getRangeReader(t,e){if(e<=this._progressiveDataLength)return null;const i=new PDFFetchStreamRangeReader(this,t,e);this._rangeRequestReaders.push(i);return i}cancelAllRequests(t){this._fullRequestReader?.cancel(t);for(const e of this._rangeRequestReaders.slice(0))e.cancel(t)}}class PDFFetchStreamReader{constructor(t){this._stream=t;this._reader=null;this._loaded=0;this._filename=null;const e=t.source;this._withCredentials=e.withCredentials||!1;this._contentLength=e.length;this._headersCapability=Promise.withResolvers();this._disableRange=e.disableRange||!1;this._rangeChunkSize=e.rangeChunkSize;this._rangeChunkSize||this._disableRange||(this._disableRange=!0);this._abortController=new AbortController;this._isStreamingSupported=!e.disableStream;this._isRangeSupported=!e.disableRange;const i=new Headers(t.headers),s=e.url;fetch(s,createFetchOptions(i,this._withCredentials,this._abortController)).then((e=>{if(!validateResponseStatus(e.status))throw createResponseStatusError(e.status,s);this._reader=e.body.getReader();this._headersCapability.resolve();const i=e.headers,{allowRangeRequests:n,suggestedLength:a}=validateRangeRequestCapabilities({responseHeaders:i,isHttp:t.isHttp,rangeChunkSize:this._rangeChunkSize,disableRange:this._disableRange});this._isRangeSupported=n;this._contentLength=a||this._contentLength;this._filename=extractFilenameFromHeader(i);!this._isStreamingSupported&&this._isRangeSupported&&this.cancel(new AbortException("Streaming is disabled."))})).catch(this._headersCapability.reject);this.onProgress=null}get headersReady(){return this._headersCapability.promise}get filename(){return this._filename}get contentLength(){return this._contentLength}get isRangeSupported(){return this._isRangeSupported}get isStreamingSupported(){return this._isStreamingSupported}async read(){await this._headersCapability.promise;const{value:t,done:e}=await this._reader.read();if(e)return{value:t,done:e};this._loaded+=t.byteLength;this.onProgress?.({loaded:this._loaded,total:this._contentLength});return{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t);this._abortController.abort()}}class PDFFetchStreamRangeReader{constructor(t,e,i){this._stream=t;this._reader=null;this._loaded=0;const s=t.source;this._withCredentials=s.withCredentials||!1;this._readCapability=Promise.withResolvers();this._isStreamingSupported=!s.disableStream;this._abortController=new AbortController;const n=new Headers(t.headers);n.append("Range",`bytes=${e}-${i-1}`);const a=s.url;fetch(a,createFetchOptions(n,this._withCredentials,this._abortController)).then((t=>{if(!validateResponseStatus(t.status))throw createResponseStatusError(t.status,a);this._readCapability.resolve();this._reader=t.body.getReader()})).catch(this._readCapability.reject);this.onProgress=null}get isStreamingSupported(){return this._isStreamingSupported}async read(){await this._readCapability.promise;const{value:t,done:e}=await this._reader.read();if(e)return{value:t,done:e};this._loaded+=t.byteLength;this.onProgress?.({loaded:this._loaded});return{value:getArrayBuffer(t),done:!1}}cancel(t){this._reader?.cancel(t);this._abortController.abort()}}class NetworkManager{constructor({url:t,httpHeaders:e,withCredentials:i}){this.url=t;this.isHttp=/^https?:/i.test(t);this.headers=createHeaders(this.isHttp,e);this.withCredentials=i||!1;this.currXhrId=0;this.pendingRequests=Object.create(null)}requestRange(t,e,i){const s={begin:t,end:e};for(const t in i)s[t]=i[t];return this.request(s)}requestFull(t){return this.request(t)}request(t){const e=new XMLHttpRequest,i=this.currXhrId++,s=this.pendingRequests[i]={xhr:e};e.open("GET",this.url);e.withCredentials=this.withCredentials;for(const[t,i]of this.headers)e.setRequestHeader(t,i);if(this.isHttp&&"begin"in t&&"end"in t){e.setRequestHeader("Range",`bytes=${t.begin}-${t.end-1}`);s.expectedStatus=206}else s.expectedStatus=200;e.responseType="arraybuffer";t.onError&&(e.onerror=function(i){t.onError(e.status)});e.onreadystatechange=this.onStateChange.bind(this,i);e.onprogress=this.onProgress.bind(this,i);s.onHeadersReceived=t.onHeadersReceived;s.onDone=t.onDone;s.onError=t.onError;s.onProgress=t.onProgress;e.send(null);return i}onProgress(t,e){const i=this.pendingRequests[t];i&&i.onProgress?.(e)}onStateChange(t,e){const i=this.pendingRequests[t];if(!i)return;const s=i.xhr;if(s.readyState>=2&&i.onHeadersReceived){i.onHeadersReceived();delete i.onHeadersReceived}if(4!==s.readyState)return;if(!(t in this.pendingRequests))return;delete this.pendingRequests[t];if(0===s.status&&this.isHttp){i.onError?.(s.status);return}const n=s.status||200;if(!(200===n&&206===i.expectedStatus)&&n!==i.expectedStatus){i.onError?.(s.status);return}const a=function network_getArrayBuffer(t){const e=t.response;return"string"!=typeof e?e:stringToBytes(e).buffer}(s);if(206===n){const t=s.getResponseHeader("Content-Range"),e=/bytes (\d+)-(\d+)\/(\d+)/.exec(t);i.onDone({begin:parseInt(e[1],10),chunk:a})}else a?i.onDone({begin:0,chunk:a}):i.onError?.(s.status)}getRequestXhr(t){return this.pendingRequests[t].xhr}isPendingRequest(t){return t in this.pendingRequests}abortRequest(t){const e=this.pendingRequests[t].xhr;delete this.pendingRequests[t];e.abort()}}class PDFNetworkStream{constructor(t){this._source=t;this._manager=new NetworkManager(t);this._rangeChunkSize=t.rangeChunkSize;this._fullRequestReader=null;this._rangeRequestReaders=[]}_onRangeRequestReaderClosed(t){const e=this._rangeRequestReaders.indexOf(t);e>=0&&this._rangeRequestReaders.splice(e,1)}getFullReader(){assert(!this._fullRequestReader,"PDFNetworkStream.getFullReader can only be called once.");this._fullRequestReader=new PDFNetworkStreamFullRequestReader(this._manager,this._source);return this._fullRequestReader}getRangeReader(t,e){const i=new PDFNetworkStreamRangeRequestReader(this._manager,t,e);i.onClosed=this._onRangeRequestReaderClosed.bind(this);this._rangeRequestReaders.push(i);return i}cancelAllRequests(t){this._fullRequestReader?.cancel(t);for(const e of this._rangeRequestReaders.slice(0))e.cancel(t)}}class PDFNetworkStreamFullRequestReader{constructor(t,e){this._manager=t;const i={onHeadersReceived:this._onHeadersReceived.bind(this),onDone:this._onDone.bind(this),onError:this._onError.bind(this),onProgress:this._onProgress.bind(this)};this._url=e.url;this._fullRequestId=t.requestFull(i);this._headersCapability=Promise.withResolvers();this._disableRange=e.disableRange||!1;this._contentLength=e.length;this._rangeChunkSize=e.rangeChunkSize;this._rangeChunkSize||this._disableRange||(this._disableRange=!0);this._isStreamingSupported=!1;this._isRangeSupported=!1;this._cachedChunks=[];this._requests=[];this._done=!1;this._storedError=void 0;this._filename=null;this.onProgress=null}_onHeadersReceived(){const t=this._fullRequestId,e=this._manager.getRequestXhr(t),i=new Headers(e.getAllResponseHeaders().trim().split(/[\r\n]+/).map((t=>{const[e,...i]=t.split(": ");return[e,i.join(": ")]}))),{allowRangeRequests:s,suggestedLength:n}=validateRangeRequestCapabilities({responseHeaders:i,isHttp:this._manager.isHttp,rangeChunkSize:this._rangeChunkSize,disableRange:this._disableRange});s&&(this._isRangeSupported=!0);this._contentLength=n||this._contentLength;this._filename=extractFilenameFromHeader(i);this._isRangeSupported&&this._manager.abortRequest(t);this._headersCapability.resolve()}_onDone(t){if(t)if(this._requests.length>0){this._requests.shift().resolve({value:t.chunk,done:!1})}else this._cachedChunks.push(t.chunk);this._done=!0;if(!(this._cachedChunks.length>0)){for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0}}_onError(t){this._storedError=createResponseStatusError(t,this._url);this._headersCapability.reject(this._storedError);for(const t of this._requests)t.reject(this._storedError);this._requests.length=0;this._cachedChunks.length=0}_onProgress(t){this.onProgress?.({loaded:t.loaded,total:t.lengthComputable?t.total:this._contentLength})}get filename(){return this._filename}get isRangeSupported(){return this._isRangeSupported}get isStreamingSupported(){return this._isStreamingSupported}get contentLength(){return this._contentLength}get headersReady(){return this._headersCapability.promise}async read(){if(this._storedError)throw this._storedError;if(this._cachedChunks.length>0){return{value:this._cachedChunks.shift(),done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;this._headersCapability.reject(t);for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0;this._manager.isPendingRequest(this._fullRequestId)&&this._manager.abortRequest(this._fullRequestId);this._fullRequestReader=null}}class PDFNetworkStreamRangeRequestReader{constructor(t,e,i){this._manager=t;const s={onDone:this._onDone.bind(this),onError:this._onError.bind(this),onProgress:this._onProgress.bind(this)};this._url=t.url;this._requestId=t.requestRange(e,i,s);this._requests=[];this._queuedChunk=null;this._done=!1;this._storedError=void 0;this.onProgress=null;this.onClosed=null}_close(){this.onClosed?.(this)}_onDone(t){const e=t.chunk;if(this._requests.length>0){this._requests.shift().resolve({value:e,done:!1})}else this._queuedChunk=e;this._done=!0;for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0;this._close()}_onError(t){this._storedError=createResponseStatusError(t,this._url);for(const t of this._requests)t.reject(this._storedError);this._requests.length=0;this._queuedChunk=null}_onProgress(t){this.isStreamingSupported||this.onProgress?.({loaded:t.loaded})}get isStreamingSupported(){return!1}async read(){if(this._storedError)throw this._storedError;if(null!==this._queuedChunk){const t=this._queuedChunk;this._queuedChunk=null;return{value:t,done:!1}}if(this._done)return{value:void 0,done:!0};const t=Promise.withResolvers();this._requests.push(t);return t.promise}cancel(t){this._done=!0;for(const t of this._requests)t.resolve({value:void 0,done:!0});this._requests.length=0;this._manager.isPendingRequest(this._requestId)&&this._manager.abortRequest(this._requestId);this._close()}}const Ut=/^[a-z][a-z0-9\-+.]+:/i;function createRequest(t,e,i){if("http:"===t.protocol){return NodePackages.get("http").request(t,{headers:e},i)}return NodePackages.get("https").request(t,{headers:e},i)}class PDFNodeStream{constructor(t){this.source=t;this.url=function parseUrlOrPath(t){if(Ut.test(t))return new URL(t);const e=NodePackages.get("url");return new URL(e.pathToFileURL(t))}(t.url);this.isHttp="http:"===this.url.protocol||"https:"===this.url.protocol;this.isFsUrl="file:"===this.url.protocol;this.headers=createHeaders(this.isHttp,t.httpHeaders);this._fullRequestReader=null;this._rangeRequestReaders=[]}get _progressiveDataLength(){return this._fullRequestReader?._loaded??0}getFullReader(){assert(!this._fullRequestReader,"PDFNodeStream.getFullReader can only be called once.");this._fullRequestReader=this.isFsUrl?new PDFNodeStreamFsFullReader(this):new PDFNodeStreamFullReader(this);return this._fullRequestReader}getRangeReader(t,e){if(e<=this._progressiveDataLength)return null;const i=this.isFsUrl?new PDFNodeStreamFsRangeReader(this,t,e):new PDFNodeStreamRangeReader(this,t,e);this._rangeRequestReaders.push(i);return i}cancelAllRequests(t){this._fullRequestReader?.cancel(t);for(const e of this._rangeRequestReaders.slice(0))e.cancel(t)}}class BaseFullReader{constructor(t){this._url=t.url;this._done=!1;this._storedError=null;this.onProgress=null;const e=t.source;this._contentLength=e.length;this._loaded=0;this._filename=null;this._disableRange=e.disableRange||!1;this._rangeChunkSize=e.rangeChunkSize;this._rangeChunkSize||this._disableRange||(this._disableRange=!0);this._isStreamingSupported=!e.disableStream;this._isRangeSupported=!e.disableRange;this._readableStream=null;this._readCapability=Promise.withResolvers();this._headersCapability=Promise.withResolvers()}get headersReady(){return this._headersCapability.promise}get filename(){return this._filename}get contentLength(){return this._contentLength}get isRangeSupported(){return this._isRangeSupported}get isStreamingSupported(){return this._isStreamingSupported}async read(){await this._readCapability.promise;if(this._done)return{value:void 0,done:!0};if(this._storedError)throw this._storedError;const t=this._readableStream.read();if(null===t){this._readCapability=Promise.withResolvers();return this.read()}this._loaded+=t.length;this.onProgress?.({loaded:this._loaded,total:this._contentLength});return{value:new Uint8Array(t).buffer,done:!1}}cancel(t){this._readableStream?this._readableStream.destroy(t):this._error(t)}_error(t){this._storedError=t;this._readCapability.resolve()}_setReadableStream(t){this._readableStream=t;t.on("readable",(()=>{this._readCapability.resolve()}));t.on("end",(()=>{t.destroy();this._done=!0;this._readCapability.resolve()}));t.on("error",(t=>{this._error(t)}));!this._isStreamingSupported&&this._isRangeSupported&&this._error(new AbortException("streaming is disabled"));this._storedError&&this._readableStream.destroy(this._storedError)}}class BaseRangeReader{constructor(t){this._url=t.url;this._done=!1;this._storedError=null;this.onProgress=null;this._loaded=0;this._readableStream=null;this._readCapability=Promise.withResolvers();const e=t.source;this._isStreamingSupported=!e.disableStream}get isStreamingSupported(){return this._isStreamingSupported}async read(){await this._readCapability.promise;if(this._done)return{value:void 0,done:!0};if(this._storedError)throw this._storedError;const t=this._readableStream.read();if(null===t){this._readCapability=Promise.withResolvers();return this.read()}this._loaded+=t.length;this.onProgress?.({loaded:this._loaded});return{value:new Uint8Array(t).buffer,done:!1}}cancel(t){this._readableStream?this._readableStream.destroy(t):this._error(t)}_error(t){this._storedError=t;this._readCapability.resolve()}_setReadableStream(t){this._readableStream=t;t.on("readable",(()=>{this._readCapability.resolve()}));t.on("end",(()=>{t.destroy();this._done=!0;this._readCapability.resolve()}));t.on("error",(t=>{this._error(t)}));this._storedError&&this._readableStream.destroy(this._storedError)}}class PDFNodeStreamFullReader extends BaseFullReader{constructor(t){super(t);const e=Object.fromEntries(t.headers);this._request=createRequest(this._url,e,(e=>{if(404===e.statusCode){const t=new MissingPDFException(`Missing PDF "${this._url}".`);this._storedError=t;this._headersCapability.reject(t);return}this._headersCapability.resolve();this._setReadableStream(e);const i=new Headers(this._readableStream.headers),{allowRangeRequests:s,suggestedLength:n}=validateRangeRequestCapabilities({responseHeaders:i,isHttp:t.isHttp,rangeChunkSize:this._rangeChunkSize,disableRange:this._disableRange});this._isRangeSupported=s;this._contentLength=n||this._contentLength;this._filename=extractFilenameFromHeader(i)}));this._request.on("error",(t=>{this._storedError=t;this._headersCapability.reject(t)}));this._request.end()}}class PDFNodeStreamRangeReader extends BaseRangeReader{constructor(t,e,i){super(t);const s=Object.fromEntries(t.headers);s.Range=`bytes=${e}-${i-1}`;this._request=createRequest(this._url,s,(t=>{if(404!==t.statusCode)this._setReadableStream(t);else{const t=new MissingPDFException(`Missing PDF "${this._url}".`);this._storedError=t}}));this._request.on("error",(t=>{this._storedError=t}));this._request.end()}}class PDFNodeStreamFsFullReader extends BaseFullReader{constructor(t){super(t);const e=NodePackages.get("fs");e.promises.lstat(this._url).then((t=>{this._contentLength=t.size;this._setReadableStream(e.createReadStream(this._url));this._headersCapability.resolve()}),(t=>{"ENOENT"===t.code&&(t=new MissingPDFException(`Missing PDF "${this._url}".`));this._storedError=t;this._headersCapability.reject(t)}))}}class PDFNodeStreamFsRangeReader extends BaseRangeReader{constructor(t,e,i){super(t);const s=NodePackages.get("fs");this._setReadableStream(s.createReadStream(this._url,{start:e,end:i-1}))}}const jt=30;class TextLayer{#Je=Promise.withResolvers();#ut=null;#Ze=!1;#ti=!!globalThis.FontInspector?.enabled;#ei=null;#ii=null;#si=0;#ni=0;#ai=null;#ri=null;#oi=0;#li=0;#hi=Object.create(null);#di=[];#ci=null;#ui=[];#pi=new WeakMap;#gi=null;static#mi=new Map;static#fi=new Map;static#bi=new WeakMap;static#Ai=null;static#vi=new Set;constructor({textContentSource:t,container:e,viewport:i}){if(t instanceof ReadableStream)this.#ci=t;else{if("object"!=typeof t)throw new Error('No "textContentSource" parameter specified.');this.#ci=new ReadableStream({start(e){e.enqueue(t);e.close()}})}this.#ut=this.#ri=e;this.#li=i.scale*(globalThis.devicePixelRatio||1);this.#oi=i.rotation;this.#ii={div:null,properties:null,ctx:null};const{pageWidth:s,pageHeight:n,pageX:a,pageY:r}=i.rawDims;this.#gi=[1,0,0,-1,-a,r+n];this.#ni=s;this.#si=n;TextLayer.#yi();setLayerDimensions(e,i);this.#Je.promise.finally((()=>{TextLayer.#vi.delete(this);this.#ii=null;this.#hi=null})).catch((()=>{}))}static get fontFamilyMap(){const{isWindows:t,isFirefox:e}=util_FeatureTest.platform;return shadow(this,"fontFamilyMap",new Map([["sans-serif",(t&&e?"Calibri, ":"")+"sans-serif"],["monospace",(t&&e?"Lucida Console, ":"")+"monospace"]]))}render(){const pump=()=>{this.#ai.read().then((({value:t,done:e})=>{if(e)this.#Je.resolve();else{this.#ei??=t.lang;Object.assign(this.#hi,t.styles);this.#wi(t.items);pump()}}),this.#Je.reject)};this.#ai=this.#ci.getReader();TextLayer.#vi.add(this);pump();return this.#Je.promise}update({viewport:t,onBefore:e=null}){const i=t.scale*(globalThis.devicePixelRatio||1),s=t.rotation;if(s!==this.#oi){e?.();this.#oi=s;setLayerDimensions(this.#ri,{rotation:s})}if(i!==this.#li){e?.();this.#li=i;const t={div:null,properties:null,ctx:TextLayer.#xi(this.#ei)};for(const e of this.#ui){t.properties=this.#pi.get(e);t.div=e;this.#_i(t)}}}cancel(){const t=new AbortException("TextLayer task cancelled.");this.#ai?.cancel(t).catch((()=>{}));this.#ai=null;this.#Je.reject(t)}get textDivs(){return this.#ui}get textContentItemsStr(){return this.#di}#wi(t){if(this.#Ze)return;this.#ii.ctx??=TextLayer.#xi(this.#ei);const e=this.#ui,i=this.#di;for(const s of t){if(e.length>1e5){warn("Ignoring additional textDivs for performance reasons.");this.#Ze=!0;return}if(void 0!==s.str){i.push(s.str);this.#Ei(s)}else if("beginMarkedContentProps"===s.type||"beginMarkedContent"===s.type){const t=this.#ut;this.#ut=document.createElement("span");this.#ut.classList.add("markedContent");null!==s.id&&this.#ut.setAttribute("id",`${s.id}`);t.append(this.#ut)}else"endMarkedContent"===s.type&&(this.#ut=this.#ut.parentNode)}}#Ei(t){const e=document.createElement("span"),i={angle:0,canvasWidth:0,hasText:""!==t.str,hasEOL:t.hasEOL,fontSize:0};this.#ui.push(e);const s=Util.transform(this.#gi,t.transform);let n=Math.atan2(s[1],s[0]);const a=this.#hi[t.fontName];a.vertical&&(n+=Math.PI/2);let r=this.#ti&&a.fontSubstitution||a.fontFamily;r=TextLayer.fontFamilyMap.get(r)||r;const o=Math.hypot(s[2],s[3]),l=o*TextLayer.#Ci(r,this.#ei);let h,d;if(0===n){h=s[4];d=s[5]-l}else{h=s[4]+l*Math.sin(n);d=s[5]-l*Math.cos(n)}const c="calc(var(--scale-factor)*",u=e.style;if(this.#ut===this.#ri){u.left=`${(100*h/this.#ni).toFixed(2)}%`;u.top=`${(100*d/this.#si).toFixed(2)}%`}else{u.left=`${c}${h.toFixed(2)}px)`;u.top=`${c}${d.toFixed(2)}px)`}u.fontSize=`${c}${(TextLayer.#Ai*o).toFixed(2)}px)`;u.fontFamily=r;i.fontSize=o;e.setAttribute("role","presentation");e.textContent=t.str;e.dir=t.dir;this.#ti&&(e.dataset.fontName=a.fontSubstitutionLoadedName||t.fontName);0!==n&&(i.angle=n*(180/Math.PI));let p=!1;if(t.str.length>1)p=!0;else if(" "!==t.str&&t.transform[0]!==t.transform[3]){const e=Math.abs(t.transform[0]),i=Math.abs(t.transform[3]);e!==i&&Math.max(e,i)/Math.min(e,i)>1.5&&(p=!0)}p&&(i.canvasWidth=a.vertical?t.height:t.width);this.#pi.set(e,i);this.#ii.div=e;this.#ii.properties=i;this.#_i(this.#ii);i.hasText&&this.#ut.append(e);if(i.hasEOL){const t=document.createElement("br");t.setAttribute("role","presentation");this.#ut.append(t)}}#_i(t){const{div:e,properties:i,ctx:s}=t,{style:n}=e;let a="";TextLayer.#Ai>1&&(a=`scale(${1/TextLayer.#Ai})`);if(0!==i.canvasWidth&&i.hasText){const{fontFamily:t}=n,{canvasWidth:r,fontSize:o}=i;TextLayer.#Si(s,o*this.#li,t);const{width:l}=s.measureText(e.textContent);l>0&&(a=`scaleX(${r*this.#li/l}) ${a}`)}0!==i.angle&&(a=`rotate(${i.angle}deg) ${a}`);a.length>0&&(n.transform=a)}static cleanup(){if(!(this.#vi.size>0)){this.#mi.clear();for(const{canvas:t}of this.#fi.values())t.remove();this.#fi.clear()}}static#xi(t=null){let e=this.#fi.get(t||="");if(!e){const i=document.createElement("canvas");i.className="hiddenCanvasElement";i.lang=t;document.body.append(i);e=i.getContext("2d",{alpha:!1,willReadFrequently:!0});this.#fi.set(t,e);this.#bi.set(e,{size:0,family:""})}return e}static#Si(t,e,i){const s=this.#bi.get(t);if(e!==s.size||i!==s.family){t.font=`${e}px ${i}`;s.size=e;s.family=i}}static#yi(){if(null!==this.#Ai)return;const t=document.createElement("div");t.style.opacity=0;t.style.lineHeight=1;t.style.fontSize="1px";t.style.position="absolute";t.textContent="X";document.body.append(t);this.#Ai=t.getBoundingClientRect().height;t.remove()}static#Ci(t,e){const i=this.#mi.get(t);if(i)return i;const s=this.#xi(e);s.canvas.width=s.canvas.height=jt;this.#Si(s,jt,t);const n=s.measureText("");let a=n.fontBoundingBoxAscent,r=Math.abs(n.fontBoundingBoxDescent);if(a){const e=a/(a+r);this.#mi.set(t,e);s.canvas.width=s.canvas.height=0;return e}s.strokeStyle="red";s.clearRect(0,0,jt,jt);s.strokeText("g",0,0);let o=s.getImageData(0,0,jt,jt).data;r=0;for(let t=o.length-1-3;t>=0;t-=4)if(o[t]>0){r=Math.ceil(t/4/jt);break}s.clearRect(0,0,jt,jt);s.strokeText("A",0,jt);o=s.getImageData(0,0,jt,jt).data;a=0;for(let t=0,e=o.length;t<e;t+=4)if(o[t]>0){a=jt-Math.floor(t/4/jt);break}s.canvas.width=s.canvas.height=0;const l=a?a/(a+r):.8;this.#mi.set(t,l);return l}}class XfaText{static textContent(t){const e=[],i={items:e,styles:Object.create(null)};!function walk(t){if(!t)return;let i=null;const s=t.name;if("#text"===s)i=t.value;else{if(!XfaText.shouldBuildText(s))return;t?.attributes?.textContent?i=t.attributes.textContent:t.value&&(i=t.value)}null!==i&&e.push({str:i});if(t.children)for(const e of t.children)walk(e)}(t);return i}static shouldBuildText(t){return!("textarea"===t||"input"===t||"option"===t||"select"===t)}}const Wt=65536,Gt=e?class NodeCanvasFactory extends BaseCanvasFactory{_createCanvas(t,e){return NodePackages.get("canvas").createCanvas(t,e)}}:class DOMCanvasFactory extends BaseCanvasFactory{constructor({ownerDocument:t=globalThis.document,enableHWA:e=!1}){super({enableHWA:e});this._document=t}_createCanvas(t,e){const i=this._document.createElement("canvas");i.width=t;i.height=e;return i}},$t=e?class NodeCMapReaderFactory extends BaseCMapReaderFactory{_fetchData(t,e){return node_utils_fetchData(t).then((t=>({cMapData:t,compressionType:e})))}}:DOMCMapReaderFactory,Vt=e?class NodeFilterFactory extends BaseFilterFactory{}:class DOMFilterFactory extends BaseFilterFactory{#Ti;#Mi;#ki;#Pi;#Fi;#Di;#v=0;constructor({docId:t,ownerDocument:e=globalThis.document}){super();this.#Pi=t;this.#Fi=e}get#w(){return this.#Mi||=new Map}get#Ri(){return this.#Di||=new Map}get#Ii(){if(!this.#ki){const t=this.#Fi.createElement("div"),{style:e}=t;e.visibility="hidden";e.contain="strict";e.width=e.height=0;e.position="absolute";e.top=e.left=0;e.zIndex=-1;const i=this.#Fi.createElementNS(ut,"svg");i.setAttribute("width",0);i.setAttribute("height",0);this.#ki=this.#Fi.createElementNS(ut,"defs");t.append(i);i.append(this.#ki);this.#Fi.body.append(t)}return this.#ki}#Li(t){if(1===t.length){const e=t[0],i=new Array(256);for(let t=0;t<256;t++)i[t]=e[t]/255;const s=i.join(",");return[s,s,s]}const[e,i,s]=t,n=new Array(256),a=new Array(256),r=new Array(256);for(let t=0;t<256;t++){n[t]=e[t]/255;a[t]=i[t]/255;r[t]=s[t]/255}return[n.join(","),a.join(","),r.join(",")]}#Ni(t){if(void 0===this.#Ti){this.#Ti="";const t=this.#Fi.URL;t!==this.#Fi.baseURI&&(isDataScheme(t)?warn('#createUrl: ignore "data:"-URL for performance reasons.'):this.#Ti=t.split("#",1)[0])}return`url(${this.#Ti}#${t})`}addFilter(t){if(!t)return"none";let e=this.#w.get(t);if(e)return e;const[i,s,n]=this.#Li(t),a=1===t.length?i:`${i}${s}${n}`;e=this.#w.get(a);if(e){this.#w.set(t,e);return e}const r=`g_${this.#Pi}_transfer_map_${this.#v++}`,o=this.#Ni(r);this.#w.set(t,o);this.#w.set(a,o);const l=this.#Oi(r);this.#Bi(i,s,n,l);return o}addHCMFilter(t,e){const i=`${t}-${e}`,s="base";let n=this.#Ri.get(s);if(n?.key===i)return n.url;if(n){n.filter?.remove();n.key=i;n.url="none";n.filter=null}else{n={key:i,url:"none",filter:null};this.#Ri.set(s,n)}if(!t||!e)return n.url;const a=this.#Hi(t);t=Util.makeHexColor(...a);const r=this.#Hi(e);e=Util.makeHexColor(...r);this.#Ii.style.color="";if("#000000"===t&&"#ffffff"===e||t===e)return n.url;const o=new Array(256);for(let t=0;t<=255;t++){const e=t/255;o[t]=e<=.03928?e/12.92:((e+.055)/1.055)**2.4}const l=o.join(","),h=`g_${this.#Pi}_hcm_filter`,d=n.filter=this.#Oi(h);this.#Bi(l,l,l,d);this.#zi(d);const getSteps=(t,e)=>{const i=a[t]/255,s=r[t]/255,n=new Array(e+1);for(let t=0;t<=e;t++)n[t]=i+t/e*(s-i);return n.join(",")};this.#Bi(getSteps(0,5),getSteps(1,5),getSteps(2,5),d);n.url=this.#Ni(h);return n.url}addAlphaFilter(t){let e=this.#w.get(t);if(e)return e;const[i]=this.#Li([t]),s=`alpha_${i}`;e=this.#w.get(s);if(e){this.#w.set(t,e);return e}const n=`g_${this.#Pi}_alpha_map_${this.#v++}`,a=this.#Ni(n);this.#w.set(t,a);this.#w.set(s,a);const r=this.#Oi(n);this.#Ui(i,r);return a}addLuminosityFilter(t){let e,i,s=this.#w.get(t||"luminosity");if(s)return s;if(t){[e]=this.#Li([t]);i=`luminosity_${e}`}else i="luminosity";s=this.#w.get(i);if(s){this.#w.set(t,s);return s}const n=`g_${this.#Pi}_luminosity_map_${this.#v++}`,a=this.#Ni(n);this.#w.set(t,a);this.#w.set(i,a);const r=this.#Oi(n);this.#ji(r);t&&this.#Ui(e,r);return a}addHighlightHCMFilter(t,e,i,s,n){const a=`${e}-${i}-${s}-${n}`;let r=this.#Ri.get(t);if(r?.key===a)return r.url;if(r){r.filter?.remove();r.key=a;r.url="none";r.filter=null}else{r={key:a,url:"none",filter:null};this.#Ri.set(t,r)}if(!e||!i)return r.url;const[o,l]=[e,i].map(this.#Hi.bind(this));let h=Math.round(.2126*o[0]+.7152*o[1]+.0722*o[2]),d=Math.round(.2126*l[0]+.7152*l[1]+.0722*l[2]),[c,u]=[s,n].map(this.#Hi.bind(this));d<h&&([h,d,c,u]=[d,h,u,c]);this.#Ii.style.color="";const getSteps=(t,e,i)=>{const s=new Array(256),n=(d-h)/i,a=t/255,r=(e-t)/(255*i);let o=0;for(let t=0;t<=i;t++){const e=Math.round(h+t*n),i=a+t*r;for(let t=o;t<=e;t++)s[t]=i;o=e+1}for(let t=o;t<256;t++)s[t]=s[o-1];return s.join(",")},p=`g_${this.#Pi}_hcm_${t}_filter`,g=r.filter=this.#Oi(p);this.#zi(g);this.#Bi(getSteps(c[0],u[0],5),getSteps(c[1],u[1],5),getSteps(c[2],u[2],5),g);r.url=this.#Ni(p);return r.url}destroy(t=!1){if(!t||0===this.#Ri.size){if(this.#ki){this.#ki.parentNode.parentNode.remove();this.#ki=null}if(this.#Mi){this.#Mi.clear();this.#Mi=null}this.#v=0}}#ji(t){const e=this.#Fi.createElementNS(ut,"feColorMatrix");e.setAttribute("type","matrix");e.setAttribute("values","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0");t.append(e)}#zi(t){const e=this.#Fi.createElementNS(ut,"feColorMatrix");e.setAttribute("type","matrix");e.setAttribute("values","0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0");t.append(e)}#Oi(t){const e=this.#Fi.createElementNS(ut,"filter");e.setAttribute("color-interpolation-filters","sRGB");e.setAttribute("id",t);this.#Ii.append(e);return e}#Wi(t,e,i){const s=this.#Fi.createElementNS(ut,e);s.setAttribute("type","discrete");s.setAttribute("tableValues",i);t.append(s)}#Bi(t,e,i,s){const n=this.#Fi.createElementNS(ut,"feComponentTransfer");s.append(n);this.#Wi(n,"feFuncR",t);this.#Wi(n,"feFuncG",e);this.#Wi(n,"feFuncB",i)}#Ui(t,e){const i=this.#Fi.createElementNS(ut,"feComponentTransfer");e.append(i);this.#Wi(i,"feFuncA",t)}#Hi(t){this.#Ii.style.color=t;return getRGB(getComputedStyle(this.#Ii).getPropertyValue("color"))}},qt=e?class NodeStandardFontDataFactory extends BaseStandardFontDataFactory{_fetchData(t){return node_utils_fetchData(t)}}:DOMStandardFontDataFactory;function getDocument(t={}){"string"==typeof t||t instanceof URL?t={url:t}:(t instanceof ArrayBuffer||ArrayBuffer.isView(t))&&(t={data:t});const i=new PDFDocumentLoadingTask,{docId:s}=i,n=t.url?function getUrlProp(t){if(t instanceof URL)return t.href;try{return new URL(t,window.location).href}catch{if(e&&"string"==typeof t)return t}throw new Error("Invalid PDF url data: either string or URL-object is expected in the url property.")}(t.url):null,a=t.data?function getDataProp(t){if(e&&"undefined"!=typeof Buffer&&t instanceof Buffer)throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`.");if(t instanceof Uint8Array&&t.byteLength===t.buffer.byteLength)return t;if("string"==typeof t)return stringToBytes(t);if(t instanceof ArrayBuffer||ArrayBuffer.isView(t)||"object"==typeof t&&!isNaN(t?.length))return new Uint8Array(t);throw new Error("Invalid PDF binary data: either TypedArray, string, or array-like object is expected in the data property.")}(t.data):null,r=t.httpHeaders||null,o=!0===t.withCredentials,l=t.password??null,h=t.range instanceof PDFDataRangeTransport?t.range:null,d=Number.isInteger(t.rangeChunkSize)&&t.rangeChunkSize>0?t.rangeChunkSize:Wt;let c=t.worker instanceof PDFWorker?t.worker:null;const u=t.verbosity,p="string"!=typeof t.docBaseUrl||isDataScheme(t.docBaseUrl)?null:t.docBaseUrl,g="string"==typeof t.cMapUrl?t.cMapUrl:null,m=!1!==t.cMapPacked,f=t.CMapReaderFactory||$t,b="string"==typeof t.standardFontDataUrl?t.standardFontDataUrl:null,A=t.StandardFontDataFactory||qt,v=!0!==t.stopAtErrors,y=Number.isInteger(t.maxImageSize)&&t.maxImageSize>-1?t.maxImageSize:-1,w=!1!==t.isEvalSupported,x="boolean"==typeof t.isOffscreenCanvasSupported?t.isOffscreenCanvasSupported:!e,_=Number.isInteger(t.canvasMaxAreaInBytes)?t.canvasMaxAreaInBytes:-1,E="boolean"==typeof t.disableFontFace?t.disableFontFace:e,C=!0===t.fontExtraProperties,S=!0===t.enableXfa,T=t.ownerDocument||globalThis.document,M=!0===t.disableRange,k=!0===t.disableStream,P=!0===t.disableAutoFetch,F=!0===t.pdfBug,D=t.CanvasFactory||Gt,R=t.FilterFactory||Vt,I=!0===t.enableHWA,L=h?h.length:t.length??NaN,N="boolean"==typeof t.useSystemFonts?t.useSystemFonts:!e&&!E,O="boolean"==typeof t.useWorkerFetch?t.useWorkerFetch:f===DOMCMapReaderFactory&&A===DOMStandardFontDataFactory&&g&&b&&isValidFetchUrl(g,document.baseURI)&&isValidFetchUrl(b,document.baseURI);t.canvasFactory&&deprecated("`canvasFactory`-instance option, please use `CanvasFactory` instead.");t.filterFactory&&deprecated("`filterFactory`-instance option, please use `FilterFactory` instead.");setVerbosityLevel(u);const B={canvasFactory:new D({ownerDocument:T,enableHWA:I}),filterFactory:new R({docId:s,ownerDocument:T}),cMapReaderFactory:O?null:new f({baseUrl:g,isCompressed:m}),standardFontDataFactory:O?null:new A({baseUrl:b})};if(!c){const t={verbosity:u,port:GlobalWorkerOptions.workerPort};c=t.port?PDFWorker.fromPort(t):new PDFWorker(t);i._worker=c}const H={docId:s,apiVersion:"4.7.76",data:a,password:l,disableAutoFetch:P,rangeChunkSize:d,length:L,docBaseUrl:p,enableXfa:S,evaluatorOptions:{maxImageSize:y,disableFontFace:E,ignoreErrors:v,isEvalSupported:w,isOffscreenCanvasSupported:x,canvasMaxAreaInBytes:_,fontExtraProperties:C,useSystemFonts:N,cMapUrl:O?g:null,standardFontDataUrl:O?b:null}},z={disableFontFace:E,fontExtraProperties:C,ownerDocument:T,pdfBug:F,styleElement:null,loadingParams:{disableAutoFetch:P,enableXfa:S}};c.promise.then((function(){if(i.destroyed)throw new Error("Loading aborted");if(c.destroyed)throw new Error("Worker was destroyed");const t=c.messageHandler.sendWithPromise("GetDocRequest",H,a?[a.buffer]:null);let l;if(h)l=new PDFDataTransportStream(h,{disableRange:M,disableStream:k});else if(!a){if(!n)throw new Error("getDocument - no `url` parameter provided.");let t;if(e){t="undefined"!=typeof fetch&&"undefined"!=typeof Response&&"body"in Response.prototype&&isValidFetchUrl(n)?PDFFetchStream:PDFNodeStream}else t=isValidFetchUrl(n)?PDFFetchStream:PDFNetworkStream;l=new t({url:n,length:L,httpHeaders:r,withCredentials:o,rangeChunkSize:d,disableRange:M,disableStream:k})}return t.then((t=>{if(i.destroyed)throw new Error("Loading aborted");if(c.destroyed)throw new Error("Worker was destroyed");const e=new MessageHandler(s,t,c.port),n=new WorkerTransport(e,i,l,z,B);i._transport=n;e.send("Ready",null)}))})).catch(i._capability.reject);return i}function isRefProxy(t){return"object"==typeof t&&Number.isInteger(t?.num)&&t.num>=0&&Number.isInteger(t?.gen)&&t.gen>=0}class PDFDocumentLoadingTask{static#Pi=0;constructor(){this._capability=Promise.withResolvers();this._transport=null;this._worker=null;this.docId="d"+PDFDocumentLoadingTask.#Pi++;this.destroyed=!1;this.onPassword=null;this.onProgress=null}get promise(){return this._capability.promise}async destroy(){this.destroyed=!0;try{this._worker?.port&&(this._worker._pendingDestroy=!0);await(this._transport?.destroy())}catch(t){this._worker?.port&&delete this._worker._pendingDestroy;throw t}this._transport=null;if(this._worker){this._worker.destroy();this._worker=null}}}class PDFDataRangeTransport{constructor(t,e,i=!1,s=null){this.length=t;this.initialData=e;this.progressiveDone=i;this.contentDispositionFilename=s;this._rangeListeners=[];this._progressListeners=[];this._progressiveReadListeners=[];this._progressiveDoneListeners=[];this._readyCapability=Promise.withResolvers()}addRangeListener(t){this._rangeListeners.push(t)}addProgressListener(t){this._progressListeners.push(t)}addProgressiveReadListener(t){this._progressiveReadListeners.push(t)}addProgressiveDoneListener(t){this._progressiveDoneListeners.push(t)}onDataRange(t,e){for(const i of this._rangeListeners)i(t,e)}onDataProgress(t,e){this._readyCapability.promise.then((()=>{for(const i of this._progressListeners)i(t,e)}))}onDataProgressiveRead(t){this._readyCapability.promise.then((()=>{for(const e of this._progressiveReadListeners)e(t)}))}onDataProgressiveDone(){this._readyCapability.promise.then((()=>{for(const t of this._progressiveDoneListeners)t()}))}transportReady(){this._readyCapability.resolve()}requestDataRange(t,e){unreachable("Abstract method PDFDataRangeTransport.requestDataRange")}abort(){}}class PDFDocumentProxy{constructor(t,e){this._pdfInfo=t;this._transport=e}get annotationStorage(){return this._transport.annotationStorage}get canvasFactory(){return this._transport.canvasFactory}get filterFactory(){return this._transport.filterFactory}get numPages(){return this._pdfInfo.numPages}get fingerprints(){return this._pdfInfo.fingerprints}get isPureXfa(){return shadow(this,"isPureXfa",!!this._transport._htmlForXfa)}get allXfaHtml(){return this._transport._htmlForXfa}getPage(t){return this._transport.getPage(t)}getPageIndex(t){return this._transport.getPageIndex(t)}getDestinations(){return this._transport.getDestinations()}getDestination(t){return this._transport.getDestination(t)}getPageLabels(){return this._transport.getPageLabels()}getPageLayout(){return this._transport.getPageLayout()}getPageMode(){return this._transport.getPageMode()}getViewerPreferences(){return this._transport.getViewerPreferences()}getOpenAction(){return this._transport.getOpenAction()}getAttachments(){return this._transport.getAttachments()}getJSActions(){return this._transport.getDocJSActions()}getOutline(){return this._transport.getOutline()}getOptionalContentConfig({intent:t="display"}={}){const{renderingIntent:e}=this._transport.getRenderingIntent(t);return this._transport.getOptionalContentConfig(e)}getPermissions(){return this._transport.getPermissions()}getMetadata(){return this._transport.getMetadata()}getMarkInfo(){return this._transport.getMarkInfo()}getData(){return this._transport.getData()}saveDocument(){return this._transport.saveDocument()}getDownloadInfo(){return this._transport.downloadInfoCapability.promise}cleanup(t=!1){return this._transport.startCleanup(t||this.isPureXfa)}destroy(){return this.loadingTask.destroy()}cachedPageNumber(t){return this._transport.cachedPageNumber(t)}get loadingParams(){return this._transport.loadingParams}get loadingTask(){return this._transport.loadingTask}getFieldObjects(){return this._transport.getFieldObjects()}hasJSActions(){return this._transport.hasJSActions()}getCalculationOrderIds(){return this._transport.getCalculationOrderIds()}}class PDFPageProxy{#Gi=null;#$i=!1;constructor(t,e,i,s=!1){this._pageIndex=t;this._pageInfo=e;this._transport=i;this._stats=s?new StatTimer:null;this._pdfBug=s;this.commonObjs=i.commonObjs;this.objs=new PDFObjects;this._maybeCleanupAfterRender=!1;this._intentStates=new Map;this.destroyed=!1}get pageNumber(){return this._pageIndex+1}get rotate(){return this._pageInfo.rotate}get ref(){return this._pageInfo.ref}get userUnit(){return this._pageInfo.userUnit}get view(){return this._pageInfo.view}getViewport({scale:t,rotation:e=this.rotate,offsetX:i=0,offsetY:s=0,dontFlip:n=!1}={}){return new PageViewport({viewBox:this.view,scale:t,rotation:e,offsetX:i,offsetY:s,dontFlip:n})}getAnnotations({intent:t="display"}={}){const{renderingIntent:e}=this._transport.getRenderingIntent(t);return this._transport.getAnnotations(this._pageIndex,e)}getJSActions(){return this._transport.getPageJSActions(this._pageIndex)}get filterFactory(){return this._transport.filterFactory}get isPureXfa(){return shadow(this,"isPureXfa",!!this._transport._htmlForXfa)}async getXfa(){return this._transport._htmlForXfa?.children[this._pageIndex]||null}render({canvasContext:t,viewport:e,intent:i="display",annotationMode:s=p.ENABLE,transform:n=null,background:a=null,optionalContentConfigPromise:r=null,annotationCanvasMap:l=null,pageColors:h=null,printAnnotationStorage:d=null,isEditing:c=!1}){this._stats?.time("Overall");const u=this._transport.getRenderingIntent(i,s,d,c),{renderingIntent:g,cacheKey:m}=u;this.#$i=!1;this.#Vi();r||=this._transport.getOptionalContentConfig(g);let f=this._intentStates.get(m);if(!f){f=Object.create(null);this._intentStates.set(m,f)}if(f.streamReaderCancelTimeout){clearTimeout(f.streamReaderCancelTimeout);f.streamReaderCancelTimeout=null}const b=!!(g&o);if(!f.displayReadyCapability){f.displayReadyCapability=Promise.withResolvers();f.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null};this._stats?.time("Page Request");this._pumpOperatorList(u)}const complete=t=>{f.renderTasks.delete(A);(this._maybeCleanupAfterRender||b)&&(this.#$i=!0);this.#qi(!b);if(t){A.capability.reject(t);this._abortOperatorList({intentState:f,reason:t instanceof Error?t:new Error(t)})}else A.capability.resolve();if(this._stats){this._stats.timeEnd("Rendering");this._stats.timeEnd("Overall");globalThis.Stats?.enabled&&globalThis.Stats.add(this.pageNumber,this._stats)}},A=new InternalRenderTask({callback:complete,params:{canvasContext:t,viewport:e,transform:n,background:a},objs:this.objs,commonObjs:this.commonObjs,annotationCanvasMap:l,operatorList:f.operatorList,pageIndex:this._pageIndex,canvasFactory:this._transport.canvasFactory,filterFactory:this._transport.filterFactory,useRequestAnimationFrame:!b,pdfBug:this._pdfBug,pageColors:h});(f.renderTasks||=new Set).add(A);const v=A.task;Promise.all([f.displayReadyCapability.promise,r]).then((([t,e])=>{if(this.destroyed)complete();else{this._stats?.time("Rendering");if(!(e.renderingIntent&g))throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` and `PDFDocumentProxy.getOptionalContentConfig` methods.");A.initializeGraphics({transparency:t,optionalContentConfig:e});A.operatorListChanged()}})).catch(complete);return v}getOperatorList({intent:t="display",annotationMode:e=p.ENABLE,printAnnotationStorage:i=null,isEditing:s=!1}={}){const n=this._transport.getRenderingIntent(t,e,i,s,!0);let a,r=this._intentStates.get(n.cacheKey);if(!r){r=Object.create(null);this._intentStates.set(n.cacheKey,r)}if(!r.opListReadCapability){a=Object.create(null);a.operatorListChanged=function operatorListChanged(){if(r.operatorList.lastChunk){r.opListReadCapability.resolve(r.operatorList);r.renderTasks.delete(a)}};r.opListReadCapability=Promise.withResolvers();(r.renderTasks||=new Set).add(a);r.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null};this._stats?.time("Page Request");this._pumpOperatorList(n)}return r.opListReadCapability.promise}streamTextContent({includeMarkedContent:t=!1,disableNormalization:e=!1}={}){return this._transport.messageHandler.sendWithStream("GetTextContent",{pageIndex:this._pageIndex,includeMarkedContent:!0===t,disableNormalization:!0===e},{highWaterMark:100,size:t=>t.items.length})}getTextContent(t={}){if(this._transport._htmlForXfa)return this.getXfa().then((t=>XfaText.textContent(t)));const e=this.streamTextContent(t);return new Promise((function(t,i){const s=e.getReader(),n={items:[],styles:Object.create(null),lang:null};!function pump(){s.read().then((function({value:e,done:i}){if(i)t(n);else{n.lang??=e.lang;Object.assign(n.styles,e.styles);n.items.push(...e.items);pump()}}),i)}()}))}getStructTree(){return this._transport.getStructTree(this._pageIndex)}_destroy(){this.destroyed=!0;const t=[];for(const e of this._intentStates.values()){this._abortOperatorList({intentState:e,reason:new Error("Page was destroyed."),force:!0});if(!e.opListReadCapability)for(const i of e.renderTasks){t.push(i.completed);i.cancel()}}this.objs.clear();this.#$i=!1;this.#Vi();return Promise.all(t)}cleanup(t=!1){this.#$i=!0;const e=this.#qi(!1);t&&e&&(this._stats&&=new StatTimer);return e}#qi(t=!1){this.#Vi();if(!this.#$i||this.destroyed)return!1;if(t){this.#Gi=setTimeout((()=>{this.#Gi=null;this.#qi(!1)}),5e3);return!1}for(const{renderTasks:t,operatorList:e}of this._intentStates.values())if(t.size>0||!e.lastChunk)return!1;this._intentStates.clear();this.objs.clear();this.#$i=!1;return!0}#Vi(){if(this.#Gi){clearTimeout(this.#Gi);this.#Gi=null}}_startRenderPage(t,e){const i=this._intentStates.get(e);if(i){this._stats?.timeEnd("Page Request");i.displayReadyCapability?.resolve(t)}}_renderPageChunk(t,e){for(let i=0,s=t.length;i<s;i++){e.operatorList.fnArray.push(t.fnArray[i]);e.operatorList.argsArray.push(t.argsArray[i])}e.operatorList.lastChunk=t.lastChunk;e.operatorList.separateAnnots=t.separateAnnots;for(const t of e.renderTasks)t.operatorListChanged();t.lastChunk&&this.#qi(!0)}_pumpOperatorList({renderingIntent:t,cacheKey:e,annotationStorageSerializable:i,modifiedIds:s}){const{map:n,transfer:a}=i,r=this._transport.messageHandler.sendWithStream("GetOperatorList",{pageIndex:this._pageIndex,intent:t,cacheKey:e,annotationStorage:n,modifiedIds:s},a).getReader(),o=this._intentStates.get(e);o.streamReader=r;const pump=()=>{r.read().then((({value:t,done:e})=>{if(e)o.streamReader=null;else if(!this._transport.destroyed){this._renderPageChunk(t,o);pump()}}),(t=>{o.streamReader=null;if(!this._transport.destroyed){if(o.operatorList){o.operatorList.lastChunk=!0;for(const t of o.renderTasks)t.operatorListChanged();this.#qi(!0)}if(o.displayReadyCapability)o.displayReadyCapability.reject(t);else{if(!o.opListReadCapability)throw t;o.opListReadCapability.reject(t)}}}))};pump()}_abortOperatorList({intentState:t,reason:e,force:i=!1}){if(t.streamReader){if(t.streamReaderCancelTimeout){clearTimeout(t.streamReaderCancelTimeout);t.streamReaderCancelTimeout=null}if(!i){if(t.renderTasks.size>0)return;if(e instanceof RenderingCancelledException){let i=100;e.extraDelay>0&&e.extraDelay<1e3&&(i+=e.extraDelay);t.streamReaderCancelTimeout=setTimeout((()=>{t.streamReaderCancelTimeout=null;this._abortOperatorList({intentState:t,reason:e,force:!0})}),i);return}}t.streamReader.cancel(new AbortException(e.message)).catch((()=>{}));t.streamReader=null;if(!this._transport.destroyed){for(const[e,i]of this._intentStates)if(i===t){this._intentStates.delete(e);break}this.cleanup()}}}get stats(){return this._stats}}class LoopbackPort{#Xi=new Set;#Ki=Promise.resolve();postMessage(t,e){const i={data:structuredClone(t,e?{transfer:e}:null)};this.#Ki.then((()=>{for(const t of this.#Xi)t.call(this,i)}))}addEventListener(t,e){this.#Xi.add(e)}removeEventListener(t,e){this.#Xi.delete(e)}terminate(){this.#Xi.clear()}}class PDFWorker{static#Yi=0;static#Qi=!1;static#Ji;static{if(e){this.#Qi=!0;GlobalWorkerOptions.workerSrc||="./pdf.worker.mjs"}this._isSameOrigin=(t,e)=>{let i;try{i=new URL(t);if(!i.origin||"null"===i.origin)return!1}catch{return!1}const s=new URL(e,i);return i.origin===s.origin};this._createCDNWrapper=t=>{const e=`await import("${t}");`;return URL.createObjectURL(new Blob([e],{type:"text/javascript"}))}}constructor({name:t=null,port:e=null,verbosity:i=getVerbosityLevel()}={}){this.name=t;this.destroyed=!1;this.verbosity=i;this._readyCapability=Promise.withResolvers();this._port=null;this._webWorker=null;this._messageHandler=null;if(e){if(PDFWorker.#Ji?.has(e))throw new Error("Cannot use more than one PDFWorker per port.");(PDFWorker.#Ji||=new WeakMap).set(e,this);this._initializeFromPort(e)}else this._initialize()}get promise(){return e?Promise.all([NodePackages.promise,this._readyCapability.promise]):this._readyCapability.promise}#Zi(){this._readyCapability.resolve();this._messageHandler.send("configure",{verbosity:this.verbosity})}get port(){return this._port}get messageHandler(){return this._messageHandler}_initializeFromPort(t){this._port=t;this._messageHandler=new MessageHandler("main","worker",t);this._messageHandler.on("ready",(function(){}));this.#Zi()}_initialize(){if(PDFWorker.#Qi||PDFWorker.#ts){this._setupFakeWorker();return}let{workerSrc:t}=PDFWorker;try{PDFWorker._isSameOrigin(window.location.href,t)||(t=PDFWorker._createCDNWrapper(new URL(t,window.location).href));const e=new Worker(t,{type:"module"}),i=new MessageHandler("main","worker",e),terminateEarly=()=>{s.abort();i.destroy();e.terminate();this.destroyed?this._readyCapability.reject(new Error("Worker was destroyed")):this._setupFakeWorker()},s=new AbortController;e.addEventListener("error",(()=>{this._webWorker||terminateEarly()}),{signal:s.signal});i.on("test",(t=>{s.abort();if(!this.destroyed&&t){this._messageHandler=i;this._port=e;this._webWorker=e;this.#Zi()}else terminateEarly()}));i.on("ready",(t=>{s.abort();if(this.destroyed)terminateEarly();else try{sendTest()}catch{this._setupFakeWorker()}}));const sendTest=()=>{const t=new Uint8Array;i.send("test",t,[t.buffer])};sendTest();return}catch{info("The worker has been disabled.")}this._setupFakeWorker()}_setupFakeWorker(){if(!PDFWorker.#Qi){warn("Setting up fake worker.");PDFWorker.#Qi=!0}PDFWorker._setupFakeWorkerGlobal.then((t=>{if(this.destroyed){this._readyCapability.reject(new Error("Worker was destroyed"));return}const e=new LoopbackPort;this._port=e;const i="fake"+PDFWorker.#Yi++,s=new MessageHandler(i+"_worker",i,e);t.setup(s,e);this._messageHandler=new MessageHandler(i,i+"_worker",e);this.#Zi()})).catch((t=>{this._readyCapability.reject(new Error(`Setting up fake worker failed: "${t.message}".`))}))}destroy(){this.destroyed=!0;if(this._webWorker){this._webWorker.terminate();this._webWorker=null}PDFWorker.#Ji?.delete(this._port);this._port=null;if(this._messageHandler){this._messageHandler.destroy();this._messageHandler=null}}static fromPort(t){if(!t?.port)throw new Error("PDFWorker.fromPort - invalid method signature.");const e=this.#Ji?.get(t.port);if(e){if(e._pendingDestroy)throw new Error("PDFWorker.fromPort - the worker is being destroyed.\nPlease remember to await `PDFDocumentLoadingTask.destroy()`-calls.");return e}return new PDFWorker(t)}static get workerSrc(){if(GlobalWorkerOptions.workerSrc)return GlobalWorkerOptions.workerSrc;throw new Error('No "GlobalWorkerOptions.workerSrc" specified.')}static get#ts(){try{return globalThis.pdfjsWorker?.WorkerMessageHandler||null}catch{return null}}static get _setupFakeWorkerGlobal(){return shadow(this,"_setupFakeWorkerGlobal",(async()=>{if(this.#ts)return this.#ts;return(await import(this.workerSrc)).WorkerMessageHandler})())}}class WorkerTransport{#es=new Map;#is=new Map;#ss=new Map;#ns=new Map;#as=null;constructor(t,e,i,s,n){this.messageHandler=t;this.loadingTask=e;this.commonObjs=new PDFObjects;this.fontLoader=new FontLoader({ownerDocument:s.ownerDocument,styleElement:s.styleElement});this.loadingParams=s.loadingParams;this._params=s;this.canvasFactory=n.canvasFactory;this.filterFactory=n.filterFactory;this.cMapReaderFactory=n.cMapReaderFactory;this.standardFontDataFactory=n.standardFontDataFactory;this.destroyed=!1;this.destroyCapability=null;this._networkStream=i;this._fullReader=null;this._lastProgress=null;this.downloadInfoCapability=Promise.withResolvers();this.setupMessageHandler()}#rs(t,e=null){const i=this.#es.get(t);if(i)return i;const s=this.messageHandler.sendWithPromise(t,e);this.#es.set(t,s);return s}get annotationStorage(){return shadow(this,"annotationStorage",new AnnotationStorage)}getRenderingIntent(t,e=p.ENABLE,i=null,s=!1,n=!1){let g=r,m=bt;switch(t){case"any":g=a;break;case"display":break;case"print":g=o;break;default:warn(`getRenderingIntent - invalid intent: ${t}`)}const f=g&o&&i instanceof PrintAnnotationStorage?i:this.annotationStorage;switch(e){case p.DISABLE:g+=d;break;case p.ENABLE:break;case p.ENABLE_FORMS:g+=l;break;case p.ENABLE_STORAGE:g+=h;m=f.serializable;break;default:warn(`getRenderingIntent - invalid annotationMode: ${e}`)}s&&(g+=c);n&&(g+=u);const{ids:b,hash:A}=f.modifiedIds;return{renderingIntent:g,cacheKey:[g,m.hash,A].join("_"),annotationStorageSerializable:m,modifiedIds:b}}destroy(){if(this.destroyCapability)return this.destroyCapability.promise;this.destroyed=!0;this.destroyCapability=Promise.withResolvers();this.#as?.reject(new Error("Worker was destroyed during onPassword callback"));const t=[];for(const e of this.#is.values())t.push(e._destroy());this.#is.clear();this.#ss.clear();this.#ns.clear();this.hasOwnProperty("annotationStorage")&&this.annotationStorage.resetModified();const e=this.messageHandler.sendWithPromise("Terminate",null);t.push(e);Promise.all(t).then((()=>{this.commonObjs.clear();this.fontLoader.clear();this.#es.clear();this.filterFactory.destroy();TextLayer.cleanup();this._networkStream?.cancelAllRequests(new AbortException("Worker was terminated."));if(this.messageHandler){this.messageHandler.destroy();this.messageHandler=null}this.destroyCapability.resolve()}),this.destroyCapability.reject);return this.destroyCapability.promise}setupMessageHandler(){const{messageHandler:t,loadingTask:e}=this;t.on("GetReader",((t,e)=>{assert(this._networkStream,"GetReader - no `IPDFStream` instance available.");this._fullReader=this._networkStream.getFullReader();this._fullReader.onProgress=t=>{this._lastProgress={loaded:t.loaded,total:t.total}};e.onPull=()=>{this._fullReader.read().then((function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}})).catch((t=>{e.error(t)}))};e.onCancel=t=>{this._fullReader.cancel(t);e.ready.catch((t=>{if(!this.destroyed)throw t}))}}));t.on("ReaderHeadersReady",(t=>{const i=Promise.withResolvers(),s=this._fullReader;s.headersReady.then((()=>{if(!s.isStreamingSupported||!s.isRangeSupported){this._lastProgress&&e.onProgress?.(this._lastProgress);s.onProgress=t=>{e.onProgress?.({loaded:t.loaded,total:t.total})}}i.resolve({isStreamingSupported:s.isStreamingSupported,isRangeSupported:s.isRangeSupported,contentLength:s.contentLength})}),i.reject);return i.promise}));t.on("GetRangeReader",((t,e)=>{assert(this._networkStream,"GetRangeReader - no `IPDFStream` instance available.");const i=this._networkStream.getRangeReader(t.begin,t.end);if(i){e.onPull=()=>{i.read().then((function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetRangeReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}})).catch((t=>{e.error(t)}))};e.onCancel=t=>{i.cancel(t);e.ready.catch((t=>{if(!this.destroyed)throw t}))}}else e.close()}));t.on("GetDoc",(({pdfInfo:t})=>{this._numPages=t.numPages;this._htmlForXfa=t.htmlForXfa;delete t.htmlForXfa;e._capability.resolve(new PDFDocumentProxy(t,this))}));t.on("DocException",(function(t){let i;switch(t.name){case"PasswordException":i=new PasswordException(t.message,t.code);break;case"InvalidPDFException":i=new InvalidPDFException(t.message);break;case"MissingPDFException":i=new MissingPDFException(t.message);break;case"UnexpectedResponseException":i=new UnexpectedResponseException(t.message,t.status);break;case"UnknownErrorException":i=new UnknownErrorException(t.message,t.details);break;default:unreachable("DocException - expected a valid Error.")}e._capability.reject(i)}));t.on("PasswordRequest",(t=>{this.#as=Promise.withResolvers();if(e.onPassword){const updatePassword=t=>{t instanceof Error?this.#as.reject(t):this.#as.resolve({password:t})};try{e.onPassword(updatePassword,t.code)}catch(t){this.#as.reject(t)}}else this.#as.reject(new PasswordException(t.message,t.code));return this.#as.promise}));t.on("DataLoaded",(t=>{e.onProgress?.({loaded:t.length,total:t.length});this.downloadInfoCapability.resolve(t)}));t.on("StartRenderPage",(t=>{if(this.destroyed)return;this.#is.get(t.pageIndex)._startRenderPage(t.transparency,t.cacheKey)}));t.on("commonobj",(([e,i,s])=>{if(this.destroyed)return null;if(this.commonObjs.has(e))return null;switch(i){case"Font":const{disableFontFace:n,fontExtraProperties:a,pdfBug:r}=this._params;if("error"in s){const t=s.error;warn(`Error during font loading: ${t}`);this.commonObjs.resolve(e,t);break}const o=r&&globalThis.FontInspector?.enabled?(t,e)=>globalThis.FontInspector.fontAdded(t,e):null,l=new FontFaceObject(s,{disableFontFace:n,inspectFont:o});this.fontLoader.bind(l).catch((()=>t.sendWithPromise("FontFallback",{id:e}))).finally((()=>{!a&&l.data&&(l.data=null);this.commonObjs.resolve(e,l)}));break;case"CopyLocalImage":const{imageRef:h}=s;assert(h,"The imageRef must be defined.");for(const t of this.#is.values())for(const[,i]of t.objs)if(i?.ref===h){if(!i.dataLen)return null;this.commonObjs.resolve(e,structuredClone(i));return i.dataLen}break;case"FontPath":case"Image":case"Pattern":this.commonObjs.resolve(e,s);break;default:throw new Error(`Got unknown common object type ${i}`)}return null}));t.on("obj",(([t,e,i,s])=>{if(this.destroyed)return;const n=this.#is.get(e);if(!n.objs.has(t))if(0!==n._intentStates.size)switch(i){case"Image":n.objs.resolve(t,s);s?.dataLen>1e7&&(n._maybeCleanupAfterRender=!0);break;case"Pattern":n.objs.resolve(t,s);break;default:throw new Error(`Got unknown object type ${i}`)}else s?.bitmap?.close()}));t.on("DocProgress",(t=>{this.destroyed||e.onProgress?.({loaded:t.loaded,total:t.total})}));t.on("FetchBuiltInCMap",(t=>this.destroyed?Promise.reject(new Error("Worker was destroyed.")):this.cMapReaderFactory?this.cMapReaderFactory.fetch(t):Promise.reject(new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter."))));t.on("FetchStandardFontData",(t=>this.destroyed?Promise.reject(new Error("Worker was destroyed.")):this.standardFontDataFactory?this.standardFontDataFactory.fetch(t):Promise.reject(new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter."))))}getData(){return this.messageHandler.sendWithPromise("GetData",null)}saveDocument(){this.annotationStorage.size<=0&&warn("saveDocument called while `annotationStorage` is empty, please use the getData-method instead.");const{map:t,transfer:e}=this.annotationStorage.serializable;return this.messageHandler.sendWithPromise("SaveDocument",{isPureXfa:!!this._htmlForXfa,numPages:this._numPages,annotationStorage:t,filename:this._fullReader?.filename??null},e).finally((()=>{this.annotationStorage.resetModified()}))}getPage(t){if(!Number.isInteger(t)||t<=0||t>this._numPages)return Promise.reject(new Error("Invalid page request."));const e=t-1,i=this.#ss.get(e);if(i)return i;const s=this.messageHandler.sendWithPromise("GetPage",{pageIndex:e}).then((i=>{if(this.destroyed)throw new Error("Transport destroyed");i.refStr&&this.#ns.set(i.refStr,t);const s=new PDFPageProxy(e,i,this,this._params.pdfBug);this.#is.set(e,s);return s}));this.#ss.set(e,s);return s}getPageIndex(t){return isRefProxy(t)?this.messageHandler.sendWithPromise("GetPageIndex",{num:t.num,gen:t.gen}):Promise.reject(new Error("Invalid pageIndex request."))}getAnnotations(t,e){return this.messageHandler.sendWithPromise("GetAnnotations",{pageIndex:t,intent:e})}getFieldObjects(){return this.#rs("GetFieldObjects")}hasJSActions(){return this.#rs("HasJSActions")}getCalculationOrderIds(){return this.messageHandler.sendWithPromise("GetCalculationOrderIds",null)}getDestinations(){return this.messageHandler.sendWithPromise("GetDestinations",null)}getDestination(t){return"string"!=typeof t?Promise.reject(new Error("Invalid destination request.")):this.messageHandler.sendWithPromise("GetDestination",{id:t})}getPageLabels(){return this.messageHandler.sendWithPromise("GetPageLabels",null)}getPageLayout(){return this.messageHandler.sendWithPromise("GetPageLayout",null)}getPageMode(){return this.messageHandler.sendWithPromise("GetPageMode",null)}getViewerPreferences(){return this.messageHandler.sendWithPromise("GetViewerPreferences",null)}getOpenAction(){return this.messageHandler.sendWithPromise("GetOpenAction",null)}getAttachments(){return this.messageHandler.sendWithPromise("GetAttachments",null)}getDocJSActions(){return this.#rs("GetDocJSActions")}getPageJSActions(t){return this.messageHandler.sendWithPromise("GetPageJSActions",{pageIndex:t})}getStructTree(t){return this.messageHandler.sendWithPromise("GetStructTree",{pageIndex:t})}getOutline(){return this.messageHandler.sendWithPromise("GetOutline",null)}getOptionalContentConfig(t){return this.#rs("GetOptionalContentConfig").then((e=>new OptionalContentConfig(e,t)))}getPermissions(){return this.messageHandler.sendWithPromise("GetPermissions",null)}getMetadata(){const t="GetMetadata",e=this.#es.get(t);if(e)return e;const i=this.messageHandler.sendWithPromise(t,null).then((t=>({info:t[0],metadata:t[1]?new Metadata(t[1]):null,contentDispositionFilename:this._fullReader?.filename??null,contentLength:this._fullReader?.contentLength??null})));this.#es.set(t,i);return i}getMarkInfo(){return this.messageHandler.sendWithPromise("GetMarkInfo",null)}async startCleanup(t=!1){if(!this.destroyed){await this.messageHandler.sendWithPromise("Cleanup",null);for(const t of this.#is.values()){if(!t.cleanup())throw new Error(`startCleanup: Page ${t.pageNumber} is currently rendering.`)}this.commonObjs.clear();t||this.fontLoader.clear();this.#es.clear();this.filterFactory.destroy(!0);TextLayer.cleanup()}}cachedPageNumber(t){if(!isRefProxy(t))return null;const e=0===t.gen?`${t.num}R`:`${t.num}R${t.gen}`;return this.#ns.get(e)??null}}const Xt=Symbol("INITIAL_DATA");class PDFObjects{#os=Object.create(null);#ls(t){return this.#os[t]||={...Promise.withResolvers(),data:Xt}}get(t,e=null){if(e){const i=this.#ls(t);i.promise.then((()=>e(i.data)));return null}const i=this.#os[t];if(!i||i.data===Xt)throw new Error(`Requesting object that isn't resolved yet ${t}.`);return i.data}has(t){const e=this.#os[t];return!!e&&e.data!==Xt}resolve(t,e=null){const i=this.#ls(t);i.data=e;i.resolve()}clear(){for(const t in this.#os){const{data:e}=this.#os[t];e?.bitmap?.close()}this.#os=Object.create(null)}*[Symbol.iterator](){for(const t in this.#os){const{data:e}=this.#os[t];e!==Xt&&(yield[t,e])}}}class RenderTask{#hs=null;constructor(t){this.#hs=t;this.onContinue=null}get promise(){return this.#hs.capability.promise}cancel(t=0){this.#hs.cancel(null,t)}get separateAnnots(){const{separateAnnots:t}=this.#hs.operatorList;if(!t)return!1;const{annotationCanvasMap:e}=this.#hs;return t.form||t.canvas&&e?.size>0}}class InternalRenderTask{#ds=null;static#cs=new WeakSet;constructor({callback:t,params:e,objs:i,commonObjs:s,annotationCanvasMap:n,operatorList:a,pageIndex:r,canvasFactory:o,filterFactory:l,useRequestAnimationFrame:h=!1,pdfBug:d=!1,pageColors:c=null}){this.callback=t;this.params=e;this.objs=i;this.commonObjs=s;this.annotationCanvasMap=n;this.operatorListIdx=null;this.operatorList=a;this._pageIndex=r;this.canvasFactory=o;this.filterFactory=l;this._pdfBug=d;this.pageColors=c;this.running=!1;this.graphicsReadyCallback=null;this.graphicsReady=!1;this._useRequestAnimationFrame=!0===h&&"undefined"!=typeof window;this.cancelled=!1;this.capability=Promise.withResolvers();this.task=new RenderTask(this);this._cancelBound=this.cancel.bind(this);this._continueBound=this._continue.bind(this);this._scheduleNextBound=this._scheduleNext.bind(this);this._nextBound=this._next.bind(this);this._canvas=e.canvasContext.canvas}get completed(){return this.capability.promise.catch((function(){}))}initializeGraphics({transparency:t=!1,optionalContentConfig:e}){if(this.cancelled)return;if(this._canvas){if(InternalRenderTask.#cs.has(this._canvas))throw new Error("Cannot use the same canvas during multiple render() operations. Use different canvas or ensure previous operations were cancelled or completed.");InternalRenderTask.#cs.add(this._canvas)}if(this._pdfBug&&globalThis.StepperManager?.enabled){this.stepper=globalThis.StepperManager.create(this._pageIndex);this.stepper.init(this.operatorList);this.stepper.nextBreakPoint=this.stepper.getNextBreakPoint()}const{canvasContext:i,viewport:s,transform:n,background:a}=this.params;this.gfx=new CanvasGraphics(i,this.commonObjs,this.objs,this.canvasFactory,this.filterFactory,{optionalContentConfig:e},this.annotationCanvasMap,this.pageColors);this.gfx.beginDrawing({transform:n,viewport:s,transparency:t,background:a});this.operatorListIdx=0;this.graphicsReady=!0;this.graphicsReadyCallback?.()}cancel(t=null,e=0){this.running=!1;this.cancelled=!0;this.gfx?.endDrawing();if(this.#ds){window.cancelAnimationFrame(this.#ds);this.#ds=null}InternalRenderTask.#cs.delete(this._canvas);this.callback(t||new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex+1}`,e))}operatorListChanged(){if(this.graphicsReady){this.stepper?.updateOperatorList(this.operatorList);this.running||this._continue()}else this.graphicsReadyCallback||=this._continueBound}_continue(){this.running=!0;this.cancelled||(this.task.onContinue?this.task.onContinue(this._scheduleNextBound):this._scheduleNext())}_scheduleNext(){this._useRequestAnimationFrame?this.#ds=window.requestAnimationFrame((()=>{this.#ds=null;this._nextBound().catch(this._cancelBound)})):Promise.resolve().then(this._nextBound).catch(this._cancelBound)}async _next(){if(!this.cancelled){this.operatorListIdx=this.gfx.executeOperatorList(this.operatorList,this.operatorListIdx,this._continueBound,this.stepper);if(this.operatorListIdx===this.operatorList.argsArray.length){this.running=!1;if(this.operatorList.lastChunk){this.gfx.endDrawing();InternalRenderTask.#cs.delete(this._canvas);this.callback()}}}}}const Kt="4.7.76",Yt="8b73b828b";function makeColorComp(t){return Math.floor(255*Math.max(0,Math.min(1,t))).toString(16).padStart(2,"0")}function scaleAndClamp(t){return Math.max(0,Math.min(255,255*t))}class ColorConverters{static CMYK_G([t,e,i,s]){return["G",1-Math.min(1,.3*t+.59*i+.11*e+s)]}static G_CMYK([t]){return["CMYK",0,0,0,1-t]}static G_RGB([t]){return["RGB",t,t,t]}static G_rgb([t]){return[t=scaleAndClamp(t),t,t]}static G_HTML([t]){const e=makeColorComp(t);return`#${e}${e}${e}`}static RGB_G([t,e,i]){return["G",.3*t+.59*e+.11*i]}static RGB_rgb(t){return t.map(scaleAndClamp)}static RGB_HTML(t){return`#${t.map(makeColorComp).join("")}`}static T_HTML(){return"#00000000"}static T_rgb(){return[null]}static CMYK_RGB([t,e,i,s]){return["RGB",1-Math.min(1,t+s),1-Math.min(1,i+s),1-Math.min(1,e+s)]}static CMYK_rgb([t,e,i,s]){return[scaleAndClamp(1-Math.min(1,t+s)),scaleAndClamp(1-Math.min(1,i+s)),scaleAndClamp(1-Math.min(1,e+s))]}static CMYK_HTML(t){const e=this.CMYK_RGB(t).slice(1);return this.RGB_HTML(e)}static RGB_CMYK([t,e,i]){const s=1-t,n=1-e,a=1-i;return["CMYK",s,n,a,Math.min(s,n,a)]}}class XfaLayer{static setupStorage(t,e,i,s,n){const a=s.getValue(e,{value:null});switch(i.name){case"textarea":null!==a.value&&(t.textContent=a.value);if("print"===n)break;t.addEventListener("input",(t=>{s.setValue(e,{value:t.target.value})}));break;case"input":if("radio"===i.attributes.type||"checkbox"===i.attributes.type){a.value===i.attributes.xfaOn?t.setAttribute("checked",!0):a.value===i.attributes.xfaOff&&t.removeAttribute("checked");if("print"===n)break;t.addEventListener("change",(t=>{s.setValue(e,{value:t.target.checked?t.target.getAttribute("xfaOn"):t.target.getAttribute("xfaOff")})}))}else{null!==a.value&&t.setAttribute("value",a.value);if("print"===n)break;t.addEventListener("input",(t=>{s.setValue(e,{value:t.target.value})}))}break;case"select":if(null!==a.value){t.setAttribute("value",a.value);for(const t of i.children)t.attributes.value===a.value?t.attributes.selected=!0:t.attributes.hasOwnProperty("selected")&&delete t.attributes.selected}t.addEventListener("input",(t=>{const i=t.target.options,n=-1===i.selectedIndex?"":i[i.selectedIndex].value;s.setValue(e,{value:n})}))}}static setAttributes({html:t,element:e,storage:i=null,intent:s,linkService:n}){const{attributes:a}=e,r=t instanceof HTMLAnchorElement;"radio"===a.type&&(a.name=`${a.name}-${s}`);for(const[e,i]of Object.entries(a))if(null!=i)switch(e){case"class":i.length&&t.setAttribute(e,i.join(" "));break;case"dataId":break;case"id":t.setAttribute("data-element-id",i);break;case"style":Object.assign(t.style,i);break;case"textContent":t.textContent=i;break;default:(!r||"href"!==e&&"newWindow"!==e)&&t.setAttribute(e,i)}r&&n.addLinkAttributes(t,a.href,a.newWindow);i&&a.dataId&&this.setupStorage(t,a.dataId,e,i)}static render(t){const e=t.annotationStorage,i=t.linkService,s=t.xfaHtml,n=t.intent||"display",a=document.createElement(s.name);s.attributes&&this.setAttributes({html:a,element:s,intent:n,linkService:i});const r="richText"!==n,o=t.div;o.append(a);if(t.viewport){const e=`matrix(${t.viewport.transform.join(",")})`;o.style.transform=e}r&&o.setAttribute("class","xfaLayer xfaFont");const l=[];if(0===s.children.length){if(s.value){const t=document.createTextNode(s.value);a.append(t);r&&XfaText.shouldBuildText(s.name)&&l.push(t)}return{textDivs:l}}const h=[[s,-1,a]];for(;h.length>0;){const[t,s,a]=h.at(-1);if(s+1===t.children.length){h.pop();continue}const o=t.children[++h.at(-1)[1]];if(null===o)continue;const{name:d}=o;if("#text"===d){const t=document.createTextNode(o.value);l.push(t);a.append(t);continue}const c=o?.attributes?.xmlns?document.createElementNS(o.attributes.xmlns,d):document.createElement(d);a.append(c);o.attributes&&this.setAttributes({html:c,element:o,storage:e,intent:n,linkService:i});if(o.children?.length>0)h.push([o,-1,c]);else if(o.value){const t=document.createTextNode(o.value);r&&XfaText.shouldBuildText(d)&&l.push(t);c.append(t)}}for(const t of o.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea"))t.setAttribute("readOnly",!0);return{textDivs:l}}static update(t){const e=`matrix(${t.viewport.transform.join(",")})`;t.div.style.transform=e;t.div.hidden=!1}}const Qt=1e3,Jt=new WeakSet;function getRectDims(t){return{width:t[2]-t[0],height:t[3]-t[1]}}class AnnotationElementFactory{static create(t){switch(t.data.annotationType){case C:return new LinkAnnotationElement(t);case E:return new TextAnnotationElement(t);case U:switch(t.data.fieldType){case"Tx":return new TextWidgetAnnotationElement(t);case"Btn":return t.data.radioButton?new RadioButtonWidgetAnnotationElement(t):t.data.checkBox?new CheckboxWidgetAnnotationElement(t):new PushButtonWidgetAnnotationElement(t);case"Ch":return new ChoiceWidgetAnnotationElement(t);case"Sig":return new SignatureWidgetAnnotationElement(t)}return new WidgetAnnotationElement(t);case H:return new PopupAnnotationElement(t);case S:return new FreeTextAnnotationElement(t);case T:return new LineAnnotationElement(t);case M:return new SquareAnnotationElement(t);case k:return new CircleAnnotationElement(t);case F:return new PolylineAnnotationElement(t);case O:return new CaretAnnotationElement(t);case B:return new InkAnnotationElement(t);case P:return new PolygonAnnotationElement(t);case D:return new HighlightAnnotationElement(t);case R:return new UnderlineAnnotationElement(t);case I:return new SquigglyAnnotationElement(t);case L:return new StrikeOutAnnotationElement(t);case N:return new StampAnnotationElement(t);case z:return new FileAttachmentAnnotationElement(t);default:return new AnnotationElement(t)}}}class AnnotationElement{#us=null;#ps=!1;#gs=null;constructor(t,{isRenderable:e=!1,ignoreBorder:i=!1,createQuadrilaterals:s=!1}={}){this.isRenderable=e;this.data=t.data;this.layer=t.layer;this.linkService=t.linkService;this.downloadManager=t.downloadManager;this.imageResourcesPath=t.imageResourcesPath;this.renderForms=t.renderForms;this.svgFactory=t.svgFactory;this.annotationStorage=t.annotationStorage;this.enableScripting=t.enableScripting;this.hasJSActions=t.hasJSActions;this._fieldObjects=t.fieldObjects;this.parent=t.parent;e&&(this.container=this._createContainer(i));s&&this._createQuadrilaterals()}static _hasPopupData({titleObj:t,contentsObj:e,richText:i}){return!!(t?.str||e?.str||i?.str)}get _isEditable(){return this.data.isEditable}get hasPopupData(){return AnnotationElement._hasPopupData(this.data)}updateEdited(t){if(!this.container)return;this.#us||={rect:this.data.rect.slice(0)};const{rect:e}=t;e&&this.#ms(e);this.#gs?.popup.updateEdited(t)}resetEdited(){if(this.#us){this.#ms(this.#us.rect);this.#gs?.popup.resetEdited();this.#us=null}}#ms(t){const{container:{style:e},data:{rect:i,rotation:s},parent:{viewport:{rawDims:{pageWidth:n,pageHeight:a,pageX:r,pageY:o}}}}=this;i?.splice(0,4,...t);const{width:l,height:h}=getRectDims(t);e.left=100*(t[0]-r)/n+"%";e.top=100*(a-t[3]+o)/a+"%";if(0===s){e.width=100*l/n+"%";e.height=100*h/a+"%"}else this.setRotation(s)}_createContainer(t){const{data:e,parent:{page:i,viewport:s}}=this,n=document.createElement("section");n.setAttribute("data-annotation-id",e.id);this instanceof WidgetAnnotationElement||(n.tabIndex=Qt);const{style:a}=n;a.zIndex=this.parent.zIndex++;e.popupRef&&n.setAttribute("aria-haspopup","dialog");e.alternativeText&&(n.title=e.alternativeText);e.noRotate&&n.classList.add("norotate");if(!e.rect||this instanceof PopupAnnotationElement){const{rotation:t}=e;e.hasOwnCanvas||0===t||this.setRotation(t,n);return n}const{width:r,height:o}=getRectDims(e.rect);if(!t&&e.borderStyle.width>0){a.borderWidth=`${e.borderStyle.width}px`;const t=e.borderStyle.horizontalCornerRadius,i=e.borderStyle.verticalCornerRadius;if(t>0||i>0){const e=`calc(${t}px * var(--scale-factor)) / calc(${i}px * var(--scale-factor))`;a.borderRadius=e}else if(this instanceof RadioButtonWidgetAnnotationElement){const t=`calc(${r}px * var(--scale-factor)) / calc(${o}px * var(--scale-factor))`;a.borderRadius=t}switch(e.borderStyle.style){case j:a.borderStyle="solid";break;case W:a.borderStyle="dashed";break;case G:warn("Unimplemented border style: beveled");break;case $:warn("Unimplemented border style: inset");break;case V:a.borderBottomStyle="solid"}const s=e.borderColor||null;if(s){this.#ps=!0;a.borderColor=Util.makeHexColor(0|s[0],0|s[1],0|s[2])}else a.borderWidth=0}const l=Util.normalizeRect([e.rect[0],i.view[3]-e.rect[1]+i.view[1],e.rect[2],i.view[3]-e.rect[3]+i.view[1]]),{pageWidth:h,pageHeight:d,pageX:c,pageY:u}=s.rawDims;a.left=100*(l[0]-c)/h+"%";a.top=100*(l[1]-u)/d+"%";const{rotation:p}=e;if(e.hasOwnCanvas||0===p){a.width=100*r/h+"%";a.height=100*o/d+"%"}else this.setRotation(p,n);return n}setRotation(t,e=this.container){if(!this.data.rect)return;const{pageWidth:i,pageHeight:s}=this.parent.viewport.rawDims,{width:n,height:a}=getRectDims(this.data.rect);let r,o;if(t%180==0){r=100*n/i;o=100*a/s}else{r=100*a/i;o=100*n/s}e.style.width=`${r}%`;e.style.height=`${o}%`;e.setAttribute("data-main-rotation",(360-t)%360)}get _commonActions(){const setColor=(t,e,i)=>{const s=i.detail[t],n=s[0],a=s.slice(1);i.target.style[e]=ColorConverters[`${n}_HTML`](a);this.annotationStorage.setValue(this.data.id,{[e]:ColorConverters[`${n}_rgb`](a)})};return shadow(this,"_commonActions",{display:t=>{const{display:e}=t.detail,i=e%2==1;this.container.style.visibility=i?"hidden":"visible";this.annotationStorage.setValue(this.data.id,{noView:i,noPrint:1===e||2===e})},print:t=>{this.annotationStorage.setValue(this.data.id,{noPrint:!t.detail.print})},hidden:t=>{const{hidden:e}=t.detail;this.container.style.visibility=e?"hidden":"visible";this.annotationStorage.setValue(this.data.id,{noPrint:e,noView:e})},focus:t=>{setTimeout((()=>t.target.focus({preventScroll:!1})),0)},userName:t=>{t.target.title=t.detail.userName},readonly:t=>{t.target.disabled=t.detail.readonly},required:t=>{this._setRequired(t.target,t.detail.required)},bgColor:t=>{setColor("bgColor","backgroundColor",t)},fillColor:t=>{setColor("fillColor","backgroundColor",t)},fgColor:t=>{setColor("fgColor","color",t)},textColor:t=>{setColor("textColor","color",t)},borderColor:t=>{setColor("borderColor","borderColor",t)},strokeColor:t=>{setColor("strokeColor","borderColor",t)},rotation:t=>{const e=t.detail.rotation;this.setRotation(e);this.annotationStorage.setValue(this.data.id,{rotation:e})}})}_dispatchEventFromSandbox(t,e){const i=this._commonActions;for(const s of Object.keys(e.detail)){const n=t[s]||i[s];n?.(e)}}_setDefaultPropertiesFromJS(t){if(!this.enableScripting)return;const e=this.annotationStorage.getRawValue(this.data.id);if(!e)return;const i=this._commonActions;for(const[s,n]of Object.entries(e)){const a=i[s];if(a){a({detail:{[s]:n},target:t});delete e[s]}}}_createQuadrilaterals(){if(!this.container)return;const{quadPoints:t}=this.data;if(!t)return;const[e,i,s,n]=this.data.rect.map((t=>Math.fround(t)));if(8===t.length){const[a,r,o,l]=t.subarray(2,6);if(s===a&&n===r&&e===o&&i===l)return}const{style:a}=this.container;let r;if(this.#ps){const{borderColor:t,borderWidth:e}=a;a.borderWidth=0;r=["url('data:image/svg+xml;utf8,",'<svg xmlns="http://www.w3.org/2000/svg"',' preserveAspectRatio="none" viewBox="0 0 1 1">',`<g fill="transparent" stroke="${t}" stroke-width="${e}">`];this.container.classList.add("hasBorder")}const o=s-e,l=n-i,{svgFactory:h}=this,d=h.createElement("svg");d.classList.add("quadrilateralsContainer");d.setAttribute("width",0);d.setAttribute("height",0);const c=h.createElement("defs");d.append(c);const u=h.createElement("clipPath"),p=`clippath_${this.data.id}`;u.setAttribute("id",p);u.setAttribute("clipPathUnits","objectBoundingBox");c.append(u);for(let i=2,s=t.length;i<s;i+=8){const s=t[i],a=t[i+1],d=t[i+2],c=t[i+3],p=h.createElement("rect"),g=(d-e)/o,m=(n-a)/l,f=(s-d)/o,b=(a-c)/l;p.setAttribute("x",g);p.setAttribute("y",m);p.setAttribute("width",f);p.setAttribute("height",b);u.append(p);r?.push(`<rect vector-effect="non-scaling-stroke" x="${g}" y="${m}" width="${f}" height="${b}"/>`)}if(this.#ps){r.push("</g></svg>')");a.backgroundImage=r.join("")}this.container.append(d);this.container.style.clipPath=`url(#${p})`}_createPopup(){const{container:t,data:e}=this;t.setAttribute("aria-haspopup","dialog");const i=this.#gs=new PopupAnnotationElement({data:{color:e.color,titleObj:e.titleObj,modificationDate:e.modificationDate,contentsObj:e.contentsObj,richText:e.richText,parentRect:e.rect,borderStyle:0,id:`popup_${e.id}`,rotation:e.rotation},parent:this.parent,elements:[this]});this.parent.div.append(i.render())}render(){unreachable("Abstract method `AnnotationElement.render` called")}_getElementsByName(t,e=null){const i=[];if(this._fieldObjects){const s=this._fieldObjects[t];if(s)for(const{page:t,id:n,exportValues:a}of s){if(-1===t)continue;if(n===e)continue;const s="string"==typeof a?a:null,r=document.querySelector(`[data-element-id="${n}"]`);!r||Jt.has(r)?i.push({id:n,exportValue:s,domElement:r}):warn(`_getElementsByName - element not allowed: ${n}`)}return i}for(const s of document.getElementsByName(t)){const{exportValue:t}=s,n=s.getAttribute("data-element-id");n!==e&&(Jt.has(s)&&i.push({id:n,exportValue:t,domElement:s}))}return i}show(){this.container&&(this.container.hidden=!1);this.popup?.maybeShow()}hide(){this.container&&(this.container.hidden=!0);this.popup?.forceHide()}getElementsToTriggerPopup(){return this.container}addHighlightArea(){const t=this.getElementsToTriggerPopup();if(Array.isArray(t))for(const e of t)e.classList.add("highlightArea");else t.classList.add("highlightArea")}_editOnDoubleClick(){if(!this._isEditable)return;const{annotationEditorType:t,data:{id:e}}=this;this.container.addEventListener("dblclick",(()=>{this.linkService.eventBus?.dispatch("switchannotationeditormode",{source:this,mode:t,editId:e})}))}}class LinkAnnotationElement extends AnnotationElement{constructor(t,e=null){super(t,{isRenderable:!0,ignoreBorder:!!e?.ignoreBorder,createQuadrilaterals:!0});this.isTooltipOnly=t.data.isTooltipOnly}render(){const{data:t,linkService:e}=this,i=document.createElement("a");i.setAttribute("data-element-id",t.id);let s=!1;if(t.url){e.addLinkAttributes(i,t.url,t.newWindow);s=!0}else if(t.action){this._bindNamedAction(i,t.action);s=!0}else if(t.attachment){this.#fs(i,t.attachment,t.attachmentDest);s=!0}else if(t.setOCGState){this.#bs(i,t.setOCGState);s=!0}else if(t.dest){this._bindLink(i,t.dest);s=!0}else{if(t.actions&&(t.actions.Action||t.actions["Mouse Up"]||t.actions["Mouse Down"])&&this.enableScripting&&this.hasJSActions){this._bindJSAction(i,t);s=!0}if(t.resetForm){this._bindResetFormAction(i,t.resetForm);s=!0}else if(this.isTooltipOnly&&!s){this._bindLink(i,"");s=!0}}this.container.classList.add("linkAnnotation");s&&this.container.append(i);return this.container}#As(){this.container.setAttribute("data-internal-link","")}_bindLink(t,e){t.href=this.linkService.getDestinationHash(e);t.onclick=()=>{e&&this.linkService.goToDestination(e);return!1};(e||""===e)&&this.#As()}_bindNamedAction(t,e){t.href=this.linkService.getAnchorUrl("");t.onclick=()=>{this.linkService.executeNamedAction(e);return!1};this.#As()}#fs(t,e,i=null){t.href=this.linkService.getAnchorUrl("");e.description&&(t.title=e.description);t.onclick=()=>{this.downloadManager?.openOrDownloadData(e.content,e.filename,i);return!1};this.#As()}#bs(t,e){t.href=this.linkService.getAnchorUrl("");t.onclick=()=>{this.linkService.executeSetOCGState(e);return!1};this.#As()}_bindJSAction(t,e){t.href=this.linkService.getAnchorUrl("");const i=new Map([["Action","onclick"],["Mouse Up","onmouseup"],["Mouse Down","onmousedown"]]);for(const s of Object.keys(e.actions)){const n=i.get(s);n&&(t[n]=()=>{this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e.id,name:s}});return!1})}t.onclick||(t.onclick=()=>!1);this.#As()}_bindResetFormAction(t,e){const i=t.onclick;i||(t.href=this.linkService.getAnchorUrl(""));this.#As();if(this._fieldObjects)t.onclick=()=>{i?.();const{fields:t,refs:s,include:n}=e,a=[];if(0!==t.length||0!==s.length){const e=new Set(s);for(const i of t){const t=this._fieldObjects[i]||[];for(const{id:i}of t)e.add(i)}for(const t of Object.values(this._fieldObjects))for(const i of t)e.has(i.id)===n&&a.push(i)}else for(const t of Object.values(this._fieldObjects))a.push(...t);const r=this.annotationStorage,o=[];for(const t of a){const{id:e}=t;o.push(e);switch(t.type){case"text":{const i=t.defaultValue||"";r.setValue(e,{value:i});break}case"checkbox":case"radiobutton":{const i=t.defaultValue===t.exportValues;r.setValue(e,{value:i});break}case"combobox":case"listbox":{const i=t.defaultValue||"";r.setValue(e,{value:i});break}default:continue}const i=document.querySelector(`[data-element-id="${e}"]`);i&&(Jt.has(i)?i.dispatchEvent(new Event("resetform")):warn(`_bindResetFormAction - element not allowed: ${e}`))}this.enableScripting&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:"app",ids:o,name:"ResetForm"}});return!1};else{warn('_bindResetFormAction - "resetForm" action not supported, ensure that the `fieldObjects` parameter is provided.');i||(t.onclick=()=>!1)}}}class TextAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0})}render(){this.container.classList.add("textAnnotation");const t=document.createElement("img");t.src=this.imageResourcesPath+"annotation-"+this.data.name.toLowerCase()+".svg";t.setAttribute("data-l10n-id","pdfjs-text-annotation-type");t.setAttribute("data-l10n-args",JSON.stringify({type:this.data.name}));!this.data.popupRef&&this.hasPopupData&&this._createPopup();this.container.append(t);return this.container}}class WidgetAnnotationElement extends AnnotationElement{render(){return this.container}showElementAndHideCanvas(t){if(this.data.hasOwnCanvas){"CANVAS"===t.previousSibling?.nodeName&&(t.previousSibling.hidden=!0);t.hidden=!1}}_getKeyModifier(t){return util_FeatureTest.platform.isMac?t.metaKey:t.ctrlKey}_setEventListener(t,e,i,s,n){i.includes("mouse")?t.addEventListener(i,(t=>{this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:this.data.id,name:s,value:n(t),shift:t.shiftKey,modifier:this._getKeyModifier(t)}})})):t.addEventListener(i,(t=>{if("blur"===i){if(!e.focused||!t.relatedTarget)return;e.focused=!1}else if("focus"===i){if(e.focused)return;e.focused=!0}n&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:this.data.id,name:s,value:n(t)}})}))}_setEventListeners(t,e,i,s){for(const[n,a]of i)if("Action"===a||this.data.actions?.[a]){"Focus"!==a&&"Blur"!==a||(e||={focused:!1});this._setEventListener(t,e,n,a,s);"Focus"!==a||this.data.actions?.Blur?"Blur"!==a||this.data.actions?.Focus||this._setEventListener(t,e,"focus","Focus",null):this._setEventListener(t,e,"blur","Blur",null)}}_setBackgroundColor(t){const e=this.data.backgroundColor||null;t.style.backgroundColor=null===e?"transparent":Util.makeHexColor(e[0],e[1],e[2])}_setTextStyle(t){const e=["left","center","right"],{fontColor:i}=this.data.defaultAppearanceData,s=this.data.defaultAppearanceData.fontSize||9,a=t.style;let r;const roundToOneDecimal=t=>Math.round(10*t)/10;if(this.data.multiLine){const t=Math.abs(this.data.rect[3]-this.data.rect[1]-2),e=t/(Math.round(t/(n*s))||1);r=Math.min(s,roundToOneDecimal(e/n))}else{const t=Math.abs(this.data.rect[3]-this.data.rect[1]-2);r=Math.min(s,roundToOneDecimal(t/n))}a.fontSize=`calc(${r}px * var(--scale-factor))`;a.color=Util.makeHexColor(i[0],i[1],i[2]);null!==this.data.textAlignment&&(a.textAlign=e[this.data.textAlignment])}_setRequired(t,e){e?t.setAttribute("required",!0):t.removeAttribute("required");t.setAttribute("aria-required",e)}}class TextWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms||t.data.hasOwnCanvas||!t.data.hasAppearance&&!!t.data.fieldValue})}setPropertyOnSiblings(t,e,i,s){const n=this.annotationStorage;for(const a of this._getElementsByName(t.name,t.id)){a.domElement&&(a.domElement[e]=i);n.setValue(a.id,{[s]:i})}}render(){const t=this.annotationStorage,e=this.data.id;this.container.classList.add("textWidgetAnnotation");let i=null;if(this.renderForms){const s=t.getValue(e,{value:this.data.fieldValue});let n=s.value||"";const a=t.getValue(e,{charLimit:this.data.maxLen}).charLimit;a&&n.length>a&&(n=n.slice(0,a));let r=s.formattedValue||this.data.textContent?.join("\n")||null;r&&this.data.comb&&(r=r.replaceAll(/\s+/g,""));const o={userValue:n,formattedValue:r,lastCommittedValue:null,commitKey:1,focused:!1};if(this.data.multiLine){i=document.createElement("textarea");i.textContent=r??n;this.data.doNotScroll&&(i.style.overflowY="hidden")}else{i=document.createElement("input");i.type="text";i.setAttribute("value",r??n);this.data.doNotScroll&&(i.style.overflowX="hidden")}this.data.hasOwnCanvas&&(i.hidden=!0);Jt.add(i);i.setAttribute("data-element-id",e);i.disabled=this.data.readOnly;i.name=this.data.fieldName;i.tabIndex=Qt;this._setRequired(i,this.data.required);a&&(i.maxLength=a);i.addEventListener("input",(s=>{t.setValue(e,{value:s.target.value});this.setPropertyOnSiblings(i,"value",s.target.value,"value");o.formattedValue=null}));i.addEventListener("resetform",(t=>{const e=this.data.defaultFieldValue??"";i.value=o.userValue=e;o.formattedValue=null}));let blurListener=t=>{const{formattedValue:e}=o;null!=e&&(t.target.value=e);t.target.scrollLeft=0};if(this.enableScripting&&this.hasJSActions){i.addEventListener("focus",(t=>{if(o.focused)return;const{target:e}=t;o.userValue&&(e.value=o.userValue);o.lastCommittedValue=e.value;o.commitKey=1;this.data.actions?.Focus||(o.focused=!0)}));i.addEventListener("updatefromsandbox",(i=>{this.showElementAndHideCanvas(i.target);const s={value(i){o.userValue=i.detail.value??"";t.setValue(e,{value:o.userValue.toString()});i.target.value=o.userValue},formattedValue(i){const{formattedValue:s}=i.detail;o.formattedValue=s;null!=s&&i.target!==document.activeElement&&(i.target.value=s);t.setValue(e,{formattedValue:s})},selRange(t){t.target.setSelectionRange(...t.detail.selRange)},charLimit:i=>{const{charLimit:s}=i.detail,{target:n}=i;if(0===s){n.removeAttribute("maxLength");return}n.setAttribute("maxLength",s);let a=o.userValue;if(a&&!(a.length<=s)){a=a.slice(0,s);n.value=o.userValue=a;t.setValue(e,{value:a});this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:a,willCommit:!0,commitKey:1,selStart:n.selectionStart,selEnd:n.selectionEnd}})}}};this._dispatchEventFromSandbox(s,i)}));i.addEventListener("keydown",(t=>{o.commitKey=1;let i=-1;"Escape"===t.key?i=0:"Enter"!==t.key||this.data.multiLine?"Tab"===t.key&&(o.commitKey=3):i=2;if(-1===i)return;const{value:s}=t.target;if(o.lastCommittedValue!==s){o.lastCommittedValue=s;o.userValue=s;this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:s,willCommit:!0,commitKey:i,selStart:t.target.selectionStart,selEnd:t.target.selectionEnd}})}}));const s=blurListener;blurListener=null;i.addEventListener("blur",(t=>{if(!o.focused||!t.relatedTarget)return;this.data.actions?.Blur||(o.focused=!1);const{value:i}=t.target;o.userValue=i;o.lastCommittedValue!==i&&this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:i,willCommit:!0,commitKey:o.commitKey,selStart:t.target.selectionStart,selEnd:t.target.selectionEnd}});s(t)}));this.data.actions?.Keystroke&&i.addEventListener("beforeinput",(t=>{o.lastCommittedValue=null;const{data:i,target:s}=t,{value:n,selectionStart:a,selectionEnd:r}=s;let l=a,h=r;switch(t.inputType){case"deleteWordBackward":{const t=n.substring(0,a).match(/\w*[^\w]*$/);t&&(l-=t[0].length);break}case"deleteWordForward":{const t=n.substring(a).match(/^[^\w]*\w*/);t&&(h+=t[0].length);break}case"deleteContentBackward":a===r&&(l-=1);break;case"deleteContentForward":a===r&&(h+=1)}t.preventDefault();this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:n,change:i||"",willCommit:!1,selStart:l,selEnd:h}})}));this._setEventListeners(i,o,[["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],(t=>t.target.value))}blurListener&&i.addEventListener("blur",blurListener);if(this.data.comb){const t=(this.data.rect[2]-this.data.rect[0])/a;i.classList.add("comb");i.style.letterSpacing=`calc(${t}px * var(--scale-factor) - 1ch)`}}else{i=document.createElement("div");i.textContent=this.data.fieldValue;i.style.verticalAlign="middle";i.style.display="table-cell";this.data.hasOwnCanvas&&(i.hidden=!0)}this._setTextStyle(i);this._setBackgroundColor(i);this._setDefaultPropertiesFromJS(i);this.container.append(i);return this.container}}class SignatureWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:!!t.data.hasOwnCanvas})}}class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){const t=this.annotationStorage,e=this.data,i=e.id;let s=t.getValue(i,{value:e.exportValue===e.fieldValue}).value;if("string"==typeof s){s="Off"!==s;t.setValue(i,{value:s})}this.container.classList.add("buttonWidgetAnnotation","checkBox");const n=document.createElement("input");Jt.add(n);n.setAttribute("data-element-id",i);n.disabled=e.readOnly;this._setRequired(n,this.data.required);n.type="checkbox";n.name=e.fieldName;s&&n.setAttribute("checked",!0);n.setAttribute("exportValue",e.exportValue);n.tabIndex=Qt;n.addEventListener("change",(s=>{const{name:n,checked:a}=s.target;for(const s of this._getElementsByName(n,i)){const i=a&&s.exportValue===e.exportValue;s.domElement&&(s.domElement.checked=i);t.setValue(s.id,{value:i})}t.setValue(i,{value:a})}));n.addEventListener("resetform",(t=>{const i=e.defaultFieldValue||"Off";t.target.checked=i===e.exportValue}));if(this.enableScripting&&this.hasJSActions){n.addEventListener("updatefromsandbox",(e=>{const s={value(e){e.target.checked="Off"!==e.detail.value;t.setValue(i,{value:e.target.checked})}};this._dispatchEventFromSandbox(s,e)}));this._setEventListeners(n,null,[["change","Validate"],["change","Action"],["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],(t=>t.target.checked))}this._setBackgroundColor(n);this._setDefaultPropertiesFromJS(n);this.container.append(n);return this.container}}class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){this.container.classList.add("buttonWidgetAnnotation","radioButton");const t=this.annotationStorage,e=this.data,i=e.id;let s=t.getValue(i,{value:e.fieldValue===e.buttonValue}).value;if("string"==typeof s){s=s!==e.buttonValue;t.setValue(i,{value:s})}if(s)for(const s of this._getElementsByName(e.fieldName,i))t.setValue(s.id,{value:!1});const n=document.createElement("input");Jt.add(n);n.setAttribute("data-element-id",i);n.disabled=e.readOnly;this._setRequired(n,this.data.required);n.type="radio";n.name=e.fieldName;s&&n.setAttribute("checked",!0);n.tabIndex=Qt;n.addEventListener("change",(e=>{const{name:s,checked:n}=e.target;for(const e of this._getElementsByName(s,i))t.setValue(e.id,{value:!1});t.setValue(i,{value:n})}));n.addEventListener("resetform",(t=>{const i=e.defaultFieldValue;t.target.checked=null!=i&&i===e.buttonValue}));if(this.enableScripting&&this.hasJSActions){const s=e.buttonValue;n.addEventListener("updatefromsandbox",(e=>{const n={value:e=>{const n=s===e.detail.value;for(const s of this._getElementsByName(e.target.name)){const e=n&&s.id===i;s.domElement&&(s.domElement.checked=e);t.setValue(s.id,{value:e})}}};this._dispatchEventFromSandbox(n,e)}));this._setEventListeners(n,null,[["change","Validate"],["change","Action"],["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"]],(t=>t.target.checked))}this._setBackgroundColor(n);this._setDefaultPropertiesFromJS(n);this.container.append(n);return this.container}}class PushButtonWidgetAnnotationElement extends LinkAnnotationElement{constructor(t){super(t,{ignoreBorder:t.data.hasAppearance})}render(){const t=super.render();t.classList.add("buttonWidgetAnnotation","pushButton");const e=t.lastChild;if(this.enableScripting&&this.hasJSActions&&e){this._setDefaultPropertiesFromJS(e);e.addEventListener("updatefromsandbox",(t=>{this._dispatchEventFromSandbox({},t)}))}return t}}class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement{constructor(t){super(t,{isRenderable:t.renderForms})}render(){this.container.classList.add("choiceWidgetAnnotation");const t=this.annotationStorage,e=this.data.id,i=t.getValue(e,{value:this.data.fieldValue}),s=document.createElement("select");Jt.add(s);s.setAttribute("data-element-id",e);s.disabled=this.data.readOnly;this._setRequired(s,this.data.required);s.name=this.data.fieldName;s.tabIndex=Qt;let n=this.data.combo&&this.data.options.length>0;if(!this.data.combo){s.size=this.data.options.length;this.data.multiSelect&&(s.multiple=!0)}s.addEventListener("resetform",(t=>{const e=this.data.defaultFieldValue;for(const t of s.options)t.selected=t.value===e}));for(const t of this.data.options){const e=document.createElement("option");e.textContent=t.displayValue;e.value=t.exportValue;if(i.value.includes(t.exportValue)){e.setAttribute("selected",!0);n=!1}s.append(e)}let a=null;if(n){const t=document.createElement("option");t.value=" ";t.setAttribute("hidden",!0);t.setAttribute("selected",!0);s.prepend(t);a=()=>{t.remove();s.removeEventListener("input",a);a=null};s.addEventListener("input",a)}const getValue=t=>{const e=t?"value":"textContent",{options:i,multiple:n}=s;return n?Array.prototype.filter.call(i,(t=>t.selected)).map((t=>t[e])):-1===i.selectedIndex?null:i[i.selectedIndex][e]};let r=getValue(!1);const getItems=t=>{const e=t.target.options;return Array.prototype.map.call(e,(t=>({displayValue:t.textContent,exportValue:t.value})))};if(this.enableScripting&&this.hasJSActions){s.addEventListener("updatefromsandbox",(i=>{const n={value(i){a?.();const n=i.detail.value,o=new Set(Array.isArray(n)?n:[n]);for(const t of s.options)t.selected=o.has(t.value);t.setValue(e,{value:getValue(!0)});r=getValue(!1)},multipleSelection(t){s.multiple=!0},remove(i){const n=s.options,a=i.detail.remove;n[a].selected=!1;s.remove(a);if(n.length>0){-1===Array.prototype.findIndex.call(n,(t=>t.selected))&&(n[0].selected=!0)}t.setValue(e,{value:getValue(!0),items:getItems(i)});r=getValue(!1)},clear(i){for(;0!==s.length;)s.remove(0);t.setValue(e,{value:null,items:[]});r=getValue(!1)},insert(i){const{index:n,displayValue:a,exportValue:o}=i.detail.insert,l=s.children[n],h=document.createElement("option");h.textContent=a;h.value=o;l?l.before(h):s.append(h);t.setValue(e,{value:getValue(!0),items:getItems(i)});r=getValue(!1)},items(i){const{items:n}=i.detail;for(;0!==s.length;)s.remove(0);for(const t of n){const{displayValue:e,exportValue:i}=t,n=document.createElement("option");n.textContent=e;n.value=i;s.append(n)}s.options.length>0&&(s.options[0].selected=!0);t.setValue(e,{value:getValue(!0),items:getItems(i)});r=getValue(!1)},indices(i){const s=new Set(i.detail.indices);for(const t of i.target.options)t.selected=s.has(t.index);t.setValue(e,{value:getValue(!0)});r=getValue(!1)},editable(t){t.target.disabled=!t.detail.editable}};this._dispatchEventFromSandbox(n,i)}));s.addEventListener("input",(i=>{const s=getValue(!0),n=getValue(!1);t.setValue(e,{value:s});i.preventDefault();this.linkService.eventBus?.dispatch("dispatcheventinsandbox",{source:this,detail:{id:e,name:"Keystroke",value:r,change:n,changeEx:s,willCommit:!1,commitKey:1,keyDown:!1}})}));this._setEventListeners(s,null,[["focus","Focus"],["blur","Blur"],["mousedown","Mouse Down"],["mouseenter","Mouse Enter"],["mouseleave","Mouse Exit"],["mouseup","Mouse Up"],["input","Action"],["input","Validate"]],(t=>t.target.value))}else s.addEventListener("input",(function(i){t.setValue(e,{value:getValue(!0)})}));this.data.combo&&this._setTextStyle(s);this._setBackgroundColor(s);this._setDefaultPropertiesFromJS(s);this.container.append(s);return this.container}}class PopupAnnotationElement extends AnnotationElement{constructor(t){const{data:e,elements:i}=t;super(t,{isRenderable:AnnotationElement._hasPopupData(e)});this.elements=i;this.popup=null}render(){this.container.classList.add("popupAnnotation");const t=this.popup=new PopupElement({container:this.container,color:this.data.color,titleObj:this.data.titleObj,modificationDate:this.data.modificationDate,contentsObj:this.data.contentsObj,richText:this.data.richText,rect:this.data.rect,parentRect:this.data.parentRect||null,parent:this.parent,elements:this.elements,open:this.data.open}),e=[];for(const i of this.elements){i.popup=t;e.push(i.data.id);i.addHighlightArea()}this.container.setAttribute("aria-controls",e.map((t=>`${it}${t}`)).join(","));return this.container}}class PopupElement{#vs=this.#ys.bind(this);#ws=this.#xs.bind(this);#_s=this.#Es.bind(this);#Cs=this.#Ss.bind(this);#Ts=null;#ut=null;#Ms=null;#ks=null;#Ps=null;#Fs=null;#Ds=null;#Rs=!1;#Is=null;#S=null;#Ls=null;#Ns=null;#Os=null;#us=null;#Bs=!1;constructor({container:t,color:e,elements:i,titleObj:s,modificationDate:n,contentsObj:a,richText:r,parent:o,rect:l,parentRect:h,open:d}){this.#ut=t;this.#Os=s;this.#Ms=a;this.#Ns=r;this.#Fs=o;this.#Ts=e;this.#Ls=l;this.#Ds=h;this.#Ps=i;this.#ks=PDFDateString.toDateObject(n);this.trigger=i.flatMap((t=>t.getElementsToTriggerPopup()));for(const t of this.trigger){t.addEventListener("click",this.#Cs);t.addEventListener("mouseenter",this.#_s);t.addEventListener("mouseleave",this.#ws);t.classList.add("popupTriggerArea")}for(const t of i)t.container?.addEventListener("keydown",this.#vs);this.#ut.hidden=!0;d&&this.#Ss()}render(){if(this.#Is)return;const t=this.#Is=document.createElement("div");t.className="popup";if(this.#Ts){const e=t.style.outlineColor=Util.makeHexColor(...this.#Ts);if(CSS.supports("background-color","color-mix(in srgb, red 30%, white)"))t.style.backgroundColor=`color-mix(in srgb, ${e} 30%, white)`;else{const e=.7;t.style.backgroundColor=Util.makeHexColor(...this.#Ts.map((t=>Math.floor(e*(255-t)+t))))}}const e=document.createElement("span");e.className="header";const i=document.createElement("h1");e.append(i);({dir:i.dir,str:i.textContent}=this.#Os);t.append(e);if(this.#ks){const t=document.createElement("span");t.classList.add("popupDate");t.setAttribute("data-l10n-id","pdfjs-annotation-date-time-string");t.setAttribute("data-l10n-args",JSON.stringify({dateObj:this.#ks.valueOf()}));e.append(t)}const s=this.#Hs;if(s){XfaLayer.render({xfaHtml:s,intent:"richText",div:t});t.lastChild.classList.add("richText","popupContent")}else{const e=this._formatContents(this.#Ms);t.append(e)}this.#ut.append(t)}get#Hs(){const t=this.#Ns,e=this.#Ms;return!t?.str||e?.str&&e.str!==t.str?null:this.#Ns.html||null}get#zs(){return this.#Hs?.attributes?.style?.fontSize||0}get#Us(){return this.#Hs?.attributes?.style?.color||null}#js(t){const e=[],i={str:t,html:{name:"div",attributes:{dir:"auto"},children:[{name:"p",children:e}]}},s={style:{color:this.#Us,fontSize:this.#zs?`calc(${this.#zs}px * var(--scale-factor))`:""}};for(const i of t.split("\n"))e.push({name:"span",value:i,attributes:s});return i}_formatContents({str:t,dir:e}){const i=document.createElement("p");i.classList.add("popupContent");i.dir=e;const s=t.split(/(?:\r\n?|\n)/);for(let t=0,e=s.length;t<e;++t){const n=s[t];i.append(document.createTextNode(n));t<e-1&&i.append(document.createElement("br"))}return i}#ys(t){t.altKey||t.shiftKey||t.ctrlKey||t.metaKey||("Enter"===t.key||"Escape"===t.key&&this.#Rs)&&this.#Ss()}updateEdited({rect:t,popupContent:e}){this.#us||={contentsObj:this.#Ms,richText:this.#Ns};t&&(this.#S=null);if(e){this.#Ns=this.#js(e);this.#Ms=null}this.#Is?.remove();this.#Is=null}resetEdited(){if(this.#us){({contentsObj:this.#Ms,richText:this.#Ns}=this.#us);this.#us=null;this.#Is?.remove();this.#Is=null;this.#S=null}}#Ws(){if(null!==this.#S)return;const{page:{view:t},viewport:{rawDims:{pageWidth:e,pageHeight:i,pageX:s,pageY:n}}}=this.#Fs;let a=!!this.#Ds,r=a?this.#Ds:this.#Ls;for(const t of this.#Ps)if(!r||null!==Util.intersect(t.data.rect,r)){r=t.data.rect;a=!0;break}const o=Util.normalizeRect([r[0],t[3]-r[1]+t[1],r[2],t[3]-r[3]+t[1]]),l=a?r[2]-r[0]+5:0,h=o[0]+l,d=o[1];this.#S=[100*(h-s)/e,100*(d-n)/i];const{style:c}=this.#ut;c.left=`${this.#S[0]}%`;c.top=`${this.#S[1]}%`}#Ss(){this.#Rs=!this.#Rs;if(this.#Rs){this.#Es();this.#ut.addEventListener("click",this.#Cs);this.#ut.addEventListener("keydown",this.#vs)}else{this.#xs();this.#ut.removeEventListener("click",this.#Cs);this.#ut.removeEventListener("keydown",this.#vs)}}#Es(){this.#Is||this.render();if(this.isVisible)this.#Rs&&this.#ut.classList.add("focused");else{this.#Ws();this.#ut.hidden=!1;this.#ut.style.zIndex=parseInt(this.#ut.style.zIndex)+1e3}}#xs(){this.#ut.classList.remove("focused");if(!this.#Rs&&this.isVisible){this.#ut.hidden=!0;this.#ut.style.zIndex=parseInt(this.#ut.style.zIndex)-1e3}}forceHide(){this.#Bs=this.isVisible;this.#Bs&&(this.#ut.hidden=!0)}maybeShow(){if(this.#Bs){this.#Is||this.#Es();this.#Bs=!1;this.#ut.hidden=!1}}get isVisible(){return!1===this.#ut.hidden}}class FreeTextAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.textContent=t.data.textContent;this.textPosition=t.data.textPosition;this.annotationEditorType=g.FREETEXT}render(){this.container.classList.add("freeTextAnnotation");if(this.textContent){const t=document.createElement("div");t.classList.add("annotationTextContent");t.setAttribute("role","comment");for(const e of this.textContent){const i=document.createElement("span");i.textContent=e;t.append(i)}this.container.append(t)}!this.data.popupRef&&this.hasPopupData&&this._createPopup();this._editOnDoubleClick();return this.container}}class LineAnnotationElement extends AnnotationElement{#Gs=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("lineAnnotation");const t=this.data,{width:e,height:i}=getRectDims(t.rect),s=this.svgFactory.create(e,i,!0),n=this.#Gs=this.svgFactory.createElement("svg:line");n.setAttribute("x1",t.rect[2]-t.lineCoordinates[0]);n.setAttribute("y1",t.rect[3]-t.lineCoordinates[1]);n.setAttribute("x2",t.rect[2]-t.lineCoordinates[2]);n.setAttribute("y2",t.rect[3]-t.lineCoordinates[3]);n.setAttribute("stroke-width",t.borderStyle.width||1);n.setAttribute("stroke","transparent");n.setAttribute("fill","transparent");s.append(n);this.container.append(s);!t.popupRef&&this.hasPopupData&&this._createPopup();return this.container}getElementsToTriggerPopup(){return this.#Gs}addHighlightArea(){this.container.classList.add("highlightArea")}}class SquareAnnotationElement extends AnnotationElement{#$s=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("squareAnnotation");const t=this.data,{width:e,height:i}=getRectDims(t.rect),s=this.svgFactory.create(e,i,!0),n=t.borderStyle.width,a=this.#$s=this.svgFactory.createElement("svg:rect");a.setAttribute("x",n/2);a.setAttribute("y",n/2);a.setAttribute("width",e-n);a.setAttribute("height",i-n);a.setAttribute("stroke-width",n||1);a.setAttribute("stroke","transparent");a.setAttribute("fill","transparent");s.append(a);this.container.append(s);!t.popupRef&&this.hasPopupData&&this._createPopup();return this.container}getElementsToTriggerPopup(){return this.#$s}addHighlightArea(){this.container.classList.add("highlightArea")}}class CircleAnnotationElement extends AnnotationElement{#Vs=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("circleAnnotation");const t=this.data,{width:e,height:i}=getRectDims(t.rect),s=this.svgFactory.create(e,i,!0),n=t.borderStyle.width,a=this.#Vs=this.svgFactory.createElement("svg:ellipse");a.setAttribute("cx",e/2);a.setAttribute("cy",i/2);a.setAttribute("rx",e/2-n/2);a.setAttribute("ry",i/2-n/2);a.setAttribute("stroke-width",n||1);a.setAttribute("stroke","transparent");a.setAttribute("fill","transparent");s.append(a);this.container.append(s);!t.popupRef&&this.hasPopupData&&this._createPopup();return this.container}getElementsToTriggerPopup(){return this.#Vs}addHighlightArea(){this.container.classList.add("highlightArea")}}class PolylineAnnotationElement extends AnnotationElement{#qs=null;constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.containerClassName="polylineAnnotation";this.svgElementName="svg:polyline"}render(){this.container.classList.add(this.containerClassName);const{data:{rect:t,vertices:e,borderStyle:i,popupRef:s}}=this;if(!e)return this.container;const{width:n,height:a}=getRectDims(t),r=this.svgFactory.create(n,a,!0);let o=[];for(let i=0,s=e.length;i<s;i+=2){const s=e[i]-t[0],n=t[3]-e[i+1];o.push(`${s},${n}`)}o=o.join(" ");const l=this.#qs=this.svgFactory.createElement(this.svgElementName);l.setAttribute("points",o);l.setAttribute("stroke-width",i.width||1);l.setAttribute("stroke","transparent");l.setAttribute("fill","transparent");r.append(l);this.container.append(r);!s&&this.hasPopupData&&this._createPopup();return this.container}getElementsToTriggerPopup(){return this.#qs}addHighlightArea(){this.container.classList.add("highlightArea")}}class PolygonAnnotationElement extends PolylineAnnotationElement{constructor(t){super(t);this.containerClassName="polygonAnnotation";this.svgElementName="svg:polygon"}}class CaretAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0})}render(){this.container.classList.add("caretAnnotation");!this.data.popupRef&&this.hasPopupData&&this._createPopup();return this.container}}class InkAnnotationElement extends AnnotationElement{#Xs=[];constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.containerClassName="inkAnnotation";this.svgElementName="svg:polyline";this.annotationEditorType="InkHighlight"===this.data.it?g.HIGHLIGHT:g.INK}render(){this.container.classList.add(this.containerClassName);const{data:{rect:t,inkLists:e,borderStyle:i,popupRef:s}}=this,{width:n,height:a}=getRectDims(t),r=this.svgFactory.create(n,a,!0);for(const n of e){let e=[];for(let i=0,s=n.length;i<s;i+=2){const s=n[i]-t[0],a=t[3]-n[i+1];e.push(`${s},${a}`)}e=e.join(" ");const a=this.svgFactory.createElement(this.svgElementName);this.#Xs.push(a);a.setAttribute("points",e);a.setAttribute("stroke-width",i.width||1);a.setAttribute("stroke","transparent");a.setAttribute("fill","transparent");!s&&this.hasPopupData&&this._createPopup();r.append(a)}this.container.append(r);this._editOnDoubleClick();return this.container}getElementsToTriggerPopup(){return this.#Xs}addHighlightArea(){this.container.classList.add("highlightArea")}}class HighlightAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0,createQuadrilaterals:!0});this.annotationEditorType=g.HIGHLIGHT}render(){!this.data.popupRef&&this.hasPopupData&&this._createPopup();this.container.classList.add("highlightAnnotation");this._editOnDoubleClick();return this.container}}class UnderlineAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0,createQuadrilaterals:!0})}render(){!this.data.popupRef&&this.hasPopupData&&this._createPopup();this.container.classList.add("underlineAnnotation");return this.container}}class SquigglyAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0,createQuadrilaterals:!0})}render(){!this.data.popupRef&&this.hasPopupData&&this._createPopup();this.container.classList.add("squigglyAnnotation");return this.container}}class StrikeOutAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0,createQuadrilaterals:!0})}render(){!this.data.popupRef&&this.hasPopupData&&this._createPopup();this.container.classList.add("strikeoutAnnotation");return this.container}}class StampAnnotationElement extends AnnotationElement{constructor(t){super(t,{isRenderable:!0,ignoreBorder:!0});this.annotationEditorType=g.STAMP}render(){this.container.classList.add("stampAnnotation");this.container.setAttribute("role","img");!this.data.popupRef&&this.hasPopupData&&this._createPopup();this._editOnDoubleClick();return this.container}}class FileAttachmentAnnotationElement extends AnnotationElement{#Ks=null;constructor(t){super(t,{isRenderable:!0});const{file:e}=this.data;this.filename=e.filename;this.content=e.content;this.linkService.eventBus?.dispatch("fileattachmentannotation",{source:this,...e})}render(){this.container.classList.add("fileAttachmentAnnotation");const{container:t,data:e}=this;let i;if(e.hasAppearance||0===e.fillAlpha)i=document.createElement("div");else{i=document.createElement("img");i.src=`${this.imageResourcesPath}annotation-${/paperclip/i.test(e.name)?"paperclip":"pushpin"}.svg`;e.fillAlpha&&e.fillAlpha<1&&(i.style=`filter: opacity(${Math.round(100*e.fillAlpha)}%);`)}i.addEventListener("dblclick",this.#Ys.bind(this));this.#Ks=i;const{isMac:s}=util_FeatureTest.platform;t.addEventListener("keydown",(t=>{"Enter"===t.key&&(s?t.metaKey:t.ctrlKey)&&this.#Ys()}));!e.popupRef&&this.hasPopupData?this._createPopup():i.classList.add("popupTriggerArea");t.append(i);return t}getElementsToTriggerPopup(){return this.#Ks}addHighlightArea(){this.container.classList.add("highlightArea")}#Ys(){this.downloadManager?.openOrDownloadData(this.content,this.filename)}}class AnnotationLayer{#Qs=null;#Js=null;#Zs=new Map;#tn=null;constructor({div:t,accessibilityManager:e,annotationCanvasMap:i,annotationEditorUIManager:s,page:n,viewport:a,structTreeLayer:r}){this.div=t;this.#Qs=e;this.#Js=i;this.#tn=r||null;this.page=n;this.viewport=a;this.zIndex=0;this._annotationEditorUIManager=s}hasEditableAnnotations(){return this.#Zs.size>0}async#en(t,e){const i=t.firstChild||t,s=i.id=`${it}${e}`,n=await(this.#tn?.getAriaAttributes(s));if(n)for(const[t,e]of n)i.setAttribute(t,e);this.div.append(t);this.#Qs?.moveElementInDOM(this.div,t,i,!1)}async render(t){const{annotations:e}=t,i=this.div;setLayerDimensions(i,this.viewport);const s=new Map,n={data:null,layer:i,linkService:t.linkService,downloadManager:t.downloadManager,imageResourcesPath:t.imageResourcesPath||"",renderForms:!1!==t.renderForms,svgFactory:new DOMSVGFactory,annotationStorage:t.annotationStorage||new AnnotationStorage,enableScripting:!0===t.enableScripting,hasJSActions:t.hasJSActions,fieldObjects:t.fieldObjects,parent:this,elements:null};for(const t of e){if(t.noHTML)continue;const e=t.annotationType===H;if(e){const e=s.get(t.id);if(!e)continue;n.elements=e}else{const{width:e,height:i}=getRectDims(t.rect);if(e<=0||i<=0)continue}n.data=t;const i=AnnotationElementFactory.create(n);if(!i.isRenderable)continue;if(!e&&t.popupRef){const e=s.get(t.popupRef);e?e.push(i):s.set(t.popupRef,[i])}const a=i.render();t.hidden&&(a.style.visibility="hidden");await this.#en(a,t.id);if(i._isEditable){this.#Zs.set(i.data.id,i);this._annotationEditorUIManager?.renderAnnotationElement(i)}}this.#in()}update({viewport:t}){const e=this.div;this.viewport=t;setLayerDimensions(e,{rotation:t.rotation});this.#in();e.hidden=!1}#in(){if(!this.#Js)return;const t=this.div;for(const[e,i]of this.#Js){const s=t.querySelector(`[data-annotation-id="${e}"]`);if(!s)continue;i.className="annotationContent";const{firstChild:n}=s;n?"CANVAS"===n.nodeName?n.replaceWith(i):n.classList.contains("annotationContent")?n.after(i):n.before(i):s.append(i)}this.#Js.clear()}getEditableAnnotations(){return Array.from(this.#Zs.values())}getEditableAnnotation(t){return this.#Zs.get(t)}}const Zt=/\r\n?|\n/g;class FreeTextEditor extends AnnotationEditor{#Ts;#sn="";#nn=`${this.id}-editor`;#an=null;#zs;static _freeTextDefaultContent="";static _internalPadding=0;static _defaultColor=null;static _defaultFontSize=10;static get _keyboardManager(){const t=FreeTextEditor.prototype,arrowChecker=t=>t.isEmpty(),e=AnnotationEditorUIManager.TRANSLATE_SMALL,i=AnnotationEditorUIManager.TRANSLATE_BIG;return shadow(this,"_keyboardManager",new KeyboardManager([[["ctrl+s","mac+meta+s","ctrl+p","mac+meta+p"],t.commitOrRemove,{bubbles:!0}],[["ctrl+Enter","mac+meta+Enter","Escape","mac+Escape"],t.commitOrRemove],[["ArrowLeft","mac+ArrowLeft"],t._translateEmpty,{args:[-e,0],checker:arrowChecker}],[["ctrl+ArrowLeft","mac+shift+ArrowLeft"],t._translateEmpty,{args:[-i,0],checker:arrowChecker}],[["ArrowRight","mac+ArrowRight"],t._translateEmpty,{args:[e,0],checker:arrowChecker}],[["ctrl+ArrowRight","mac+shift+ArrowRight"],t._translateEmpty,{args:[i,0],checker:arrowChecker}],[["ArrowUp","mac+ArrowUp"],t._translateEmpty,{args:[0,-e],checker:arrowChecker}],[["ctrl+ArrowUp","mac+shift+ArrowUp"],t._translateEmpty,{args:[0,-i],checker:arrowChecker}],[["ArrowDown","mac+ArrowDown"],t._translateEmpty,{args:[0,e],checker:arrowChecker}],[["ctrl+ArrowDown","mac+shift+ArrowDown"],t._translateEmpty,{args:[0,i],checker:arrowChecker}]]))}static _type="freetext";static _editorType=g.FREETEXT;constructor(t){super({...t,name:"freeTextEditor"});this.#Ts=t.color||FreeTextEditor._defaultColor||AnnotationEditor._defaultLineColor;this.#zs=t.fontSize||FreeTextEditor._defaultFontSize}static initialize(t,e){AnnotationEditor.initialize(t,e,{strings:["pdfjs-free-text-default-content"]});const i=getComputedStyle(document.documentElement);this._internalPadding=parseFloat(i.getPropertyValue("--freetext-padding"))}static updateDefaultParams(t,e){switch(t){case m.FREETEXT_SIZE:FreeTextEditor._defaultFontSize=e;break;case m.FREETEXT_COLOR:FreeTextEditor._defaultColor=e}}updateParams(t,e){switch(t){case m.FREETEXT_SIZE:this.#rn(e);break;case m.FREETEXT_COLOR:this.#on(e)}}static get defaultPropertiesToUpdate(){return[[m.FREETEXT_SIZE,FreeTextEditor._defaultFontSize],[m.FREETEXT_COLOR,FreeTextEditor._defaultColor||AnnotationEditor._defaultLineColor]]}get propertiesToUpdate(){return[[m.FREETEXT_SIZE,this.#zs],[m.FREETEXT_COLOR,this.#Ts]]}#rn(t){const setFontsize=t=>{this.editorDiv.style.fontSize=`calc(${t}px * var(--scale-factor))`;this.translate(0,-(t-this.#zs)*this.parentScale);this.#zs=t;this.#ln()},e=this.#zs;this.addCommands({cmd:setFontsize.bind(this,t),undo:setFontsize.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.FREETEXT_SIZE,overwriteIfSameType:!0,keepUndo:!0})}#on(t){const setColor=t=>{this.#Ts=this.editorDiv.style.color=t},e=this.#Ts;this.addCommands({cmd:setColor.bind(this,t),undo:setColor.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.FREETEXT_COLOR,overwriteIfSameType:!0,keepUndo:!0})}_translateEmpty(t,e){this._uiManager.translateSelectedEditors(t,e,!0)}getInitialTranslation(){const t=this.parentScale;return[-FreeTextEditor._internalPadding*t,-(FreeTextEditor._internalPadding+this.#zs)*t]}rebuild(){if(this.parent){super.rebuild();null!==this.div&&(this.isAttachedToDOM||this.parent.add(this))}}enableEditMode(){if(this.isInEditMode())return;this.parent.setEditingState(!1);this.parent.updateToolbar(g.FREETEXT);super.enableEditMode();this.overlayDiv.classList.remove("enabled");this.editorDiv.contentEditable=!0;this._isDraggable=!1;this.div.removeAttribute("aria-activedescendant");this.#an=new AbortController;const t=this._uiManager.combinedSignal(this.#an);this.editorDiv.addEventListener("keydown",this.editorDivKeydown.bind(this),{signal:t});this.editorDiv.addEventListener("focus",this.editorDivFocus.bind(this),{signal:t});this.editorDiv.addEventListener("blur",this.editorDivBlur.bind(this),{signal:t});this.editorDiv.addEventListener("input",this.editorDivInput.bind(this),{signal:t});this.editorDiv.addEventListener("paste",this.editorDivPaste.bind(this),{signal:t})}disableEditMode(){if(this.isInEditMode()){this.parent.setEditingState(!0);super.disableEditMode();this.overlayDiv.classList.add("enabled");this.editorDiv.contentEditable=!1;this.div.setAttribute("aria-activedescendant",this.#nn);this._isDraggable=!0;this.#an?.abort();this.#an=null;this.div.focus({preventScroll:!0});this.isEditing=!1;this.parent.div.classList.add("freetextEditing")}}focusin(t){if(this._focusEventsAllowed){super.focusin(t);t.target!==this.editorDiv&&this.editorDiv.focus()}}onceAdded(){if(!this.width){this.enableEditMode();this.editorDiv.focus();this._initialOptions?.isCentered&&this.center();this._initialOptions=null}}isEmpty(){return!this.editorDiv||""===this.editorDiv.innerText.trim()}remove(){this.isEditing=!1;if(this.parent){this.parent.setEditingState(!0);this.parent.div.classList.add("freetextEditing")}super.remove()}#hn(){const t=[];this.editorDiv.normalize();let e=null;for(const i of this.editorDiv.childNodes)if(e?.nodeType!==Node.TEXT_NODE||"BR"!==i.nodeName){t.push(FreeTextEditor.#dn(i));e=i}return t.join("\n")}#ln(){const[t,e]=this.parentDimensions;let i;if(this.isAttachedToDOM)i=this.div.getBoundingClientRect();else{const{currentLayer:t,div:e}=this,s=e.style.display,n=e.classList.contains("hidden");e.classList.remove("hidden");e.style.display="hidden";t.div.append(this.div);i=e.getBoundingClientRect();e.remove();e.style.display=s;e.classList.toggle("hidden",n)}if(this.rotation%180==this.parentRotation%180){this.width=i.width/t;this.height=i.height/e}else{this.width=i.height/t;this.height=i.width/e}this.fixAndSetPosition()}commit(){if(!this.isInEditMode())return;super.commit();this.disableEditMode();const t=this.#sn,e=this.#sn=this.#hn().trimEnd();if(t===e)return;const setText=t=>{this.#sn=t;if(t){this.#cn();this._uiManager.rebuild(this);this.#ln()}else this.remove()};this.addCommands({cmd:()=>{setText(e)},undo:()=>{setText(t)},mustExec:!1});this.#ln()}shouldGetKeyboardEvents(){return this.isInEditMode()}enterInEditMode(){this.enableEditMode();this.editorDiv.focus()}dblclick(t){this.enterInEditMode()}keydown(t){if(t.target===this.div&&"Enter"===t.key){this.enterInEditMode();t.preventDefault()}}editorDivKeydown(t){FreeTextEditor._keyboardManager.exec(this,t)}editorDivFocus(t){this.isEditing=!0}editorDivBlur(t){this.isEditing=!1}editorDivInput(t){this.parent.div.classList.toggle("freetextEditing",this.isEmpty())}disableEditing(){this.editorDiv.setAttribute("role","comment");this.editorDiv.removeAttribute("aria-multiline")}enableEditing(){this.editorDiv.setAttribute("role","textbox");this.editorDiv.setAttribute("aria-multiline",!0)}render(){if(this.div)return this.div;let t,e;if(this.width){t=this.x;e=this.y}super.render();this.editorDiv=document.createElement("div");this.editorDiv.className="internal";this.editorDiv.setAttribute("id",this.#nn);this.editorDiv.setAttribute("data-l10n-id","pdfjs-free-text");this.enableEditing();AnnotationEditor._l10nPromise.get("pdfjs-free-text-default-content").then((t=>this.editorDiv?.setAttribute("default-content",t)));this.editorDiv.contentEditable=!0;const{style:i}=this.editorDiv;i.fontSize=`calc(${this.#zs}px * var(--scale-factor))`;i.color=this.#Ts;this.div.append(this.editorDiv);this.overlayDiv=document.createElement("div");this.overlayDiv.classList.add("overlay","enabled");this.div.append(this.overlayDiv);bindEvents(this,this.div,["dblclick","keydown"]);if(this.width){const[i,s]=this.parentDimensions;if(this.annotationElementId){const{position:n}=this._initialData;let[a,r]=this.getInitialTranslation();[a,r]=this.pageTranslationToScreen(a,r);const[o,l]=this.pageDimensions,[h,d]=this.pageTranslation;let c,u;switch(this.rotation){case 0:c=t+(n[0]-h)/o;u=e+this.height-(n[1]-d)/l;break;case 90:c=t+(n[0]-h)/o;u=e-(n[1]-d)/l;[a,r]=[r,-a];break;case 180:c=t-this.width+(n[0]-h)/o;u=e-(n[1]-d)/l;[a,r]=[-a,-r];break;case 270:c=t+(n[0]-h-this.height*l)/o;u=e+(n[1]-d-this.width*o)/l;[a,r]=[-r,a]}this.setAt(c*i,u*s,a,r)}else this.setAt(t*i,e*s,this.width*i,this.height*s);this.#cn();this._isDraggable=!0;this.editorDiv.contentEditable=!1}else{this._isDraggable=!1;this.editorDiv.contentEditable=!0}return this.div}static#dn(t){return(t.nodeType===Node.TEXT_NODE?t.nodeValue:t.innerText).replaceAll(Zt,"")}editorDivPaste(t){const e=t.clipboardData||window.clipboardData,{types:i}=e;if(1===i.length&&"text/plain"===i[0])return;t.preventDefault();const s=FreeTextEditor.#un(e.getData("text")||"").replaceAll(Zt,"\n");if(!s)return;const n=window.getSelection();if(!n.rangeCount)return;this.editorDiv.normalize();n.deleteFromDocument();const a=n.getRangeAt(0);if(!s.includes("\n")){a.insertNode(document.createTextNode(s));this.editorDiv.normalize();n.collapseToStart();return}const{startContainer:r,startOffset:o}=a,l=[],h=[];if(r.nodeType===Node.TEXT_NODE){const t=r.parentElement;h.push(r.nodeValue.slice(o).replaceAll(Zt,""));if(t!==this.editorDiv){let e=l;for(const i of this.editorDiv.childNodes)i!==t?e.push(FreeTextEditor.#dn(i)):e=h}l.push(r.nodeValue.slice(0,o).replaceAll(Zt,""))}else if(r===this.editorDiv){let t=l,e=0;for(const i of this.editorDiv.childNodes){e++===o&&(t=h);t.push(FreeTextEditor.#dn(i))}}this.#sn=`${l.join("\n")}${s}${h.join("\n")}`;this.#cn();const d=new Range;let c=l.reduce(((t,e)=>t+e.length),0);for(const{firstChild:t}of this.editorDiv.childNodes)if(t.nodeType===Node.TEXT_NODE){const e=t.nodeValue.length;if(c<=e){d.setStart(t,c);d.setEnd(t,c);break}c-=e}n.removeAllRanges();n.addRange(d)}#cn(){this.editorDiv.replaceChildren();if(this.#sn)for(const t of this.#sn.split("\n")){const e=document.createElement("div");e.append(t?document.createTextNode(t):document.createElement("br"));this.editorDiv.append(e)}}#pn(){return this.#sn.replaceAll(" "," ")}static#un(t){return t.replaceAll(" "," ")}get contentDiv(){return this.editorDiv}static async deserialize(t,e,i){let s=null;if(t instanceof FreeTextAnnotationElement){const{data:{defaultAppearanceData:{fontSize:e,fontColor:i},rect:n,rotation:a,id:r,popupRef:o},textContent:l,textPosition:h,parent:{page:{pageNumber:d}}}=t;if(!l||0===l.length)return null;s=t={annotationType:g.FREETEXT,color:Array.from(i),fontSize:e,value:l.join("\n"),position:h,pageIndex:d-1,rect:n.slice(0),rotation:a,id:r,deleted:!1,popupRef:o}}const n=await super.deserialize(t,e,i);n.#zs=t.fontSize;n.#Ts=Util.makeHexColor(...t.color);n.#sn=FreeTextEditor.#un(t.value);n.annotationElementId=t.id||null;n._initialData=s;return n}serialize(t=!1){if(this.isEmpty())return null;if(this.deleted)return this.serializeDeleted();const e=FreeTextEditor._internalPadding*this.parentScale,i=this.getRect(e,e),s=AnnotationEditor._colorManager.convert(this.isAttachedToDOM?getComputedStyle(this.editorDiv).color:this.#Ts),n={annotationType:g.FREETEXT,color:s,fontSize:this.#zs,value:this.#pn(),pageIndex:this.pageIndex,rect:i,rotation:this.rotation,structTreeParentId:this._structTreeParentId};if(t)return n;if(this.annotationElementId&&!this.#gn(n))return null;n.id=this.annotationElementId;return n}#gn(t){const{value:e,fontSize:i,color:s,pageIndex:n}=this._initialData;return this._hasBeenMoved||t.value!==e||t.fontSize!==i||t.color.some(((t,e)=>t!==s[e]))||t.pageIndex!==n}renderAnnotationElement(t){const e=super.renderAnnotationElement(t);if(this.deleted)return e;const{style:i}=e;i.fontSize=`calc(${this.#zs}px * var(--scale-factor))`;i.color=this.#Ts;e.replaceChildren();for(const t of this.#sn.split("\n")){const i=document.createElement("div");i.append(t?document.createTextNode(t):document.createElement("br"));e.append(i)}const s=FreeTextEditor._internalPadding*this.parentScale;t.updateEdited({rect:this.getRect(s,s),popupContent:this.#sn});return e}resetAnnotationElement(t){super.resetAnnotationElement(t);t.resetEdited()}}class Outliner{#mn;#fn=[];#bn=[];constructor(t,e=0,i=0,s=!0){let n=1/0,a=-1/0,r=1/0,o=-1/0;const l=10**-4;for(const{x:i,y:s,width:h,height:d}of t){const t=Math.floor((i-e)/l)*l,c=Math.ceil((i+h+e)/l)*l,u=Math.floor((s-e)/l)*l,p=Math.ceil((s+d+e)/l)*l,g=[t,u,p,!0],m=[c,u,p,!1];this.#fn.push(g,m);n=Math.min(n,t);a=Math.max(a,c);r=Math.min(r,u);o=Math.max(o,p)}const h=a-n+2*i,d=o-r+2*i,c=n-i,u=r-i,p=this.#fn.at(s?-1:-2),g=[p[0],p[2]];for(const t of this.#fn){const[e,i,s]=t;t[0]=(e-c)/h;t[1]=(i-u)/d;t[2]=(s-u)/d}this.#mn={x:c,y:u,width:h,height:d,lastPoint:g}}getOutlines(){this.#fn.sort(((t,e)=>t[0]-e[0]||t[1]-e[1]||t[2]-e[2]));const t=[];for(const e of this.#fn)if(e[3]){t.push(...this.#An(e));this.#vn(e)}else{this.#yn(e);t.push(...this.#An(e))}return this.#wn(t)}#wn(t){const e=[],i=new Set;for(const i of t){const[t,s,n]=i;e.push([t,s,i],[t,n,i])}e.sort(((t,e)=>t[1]-e[1]||t[0]-e[0]));for(let t=0,s=e.length;t<s;t+=2){const s=e[t][2],n=e[t+1][2];s.push(n);n.push(s);i.add(s);i.add(n)}const s=[];let n;for(;i.size>0;){const t=i.values().next().value;let[e,a,r,o,l]=t;i.delete(t);let h=e,d=a;n=[e,r];s.push(n);for(;;){let t;if(i.has(o))t=o;else{if(!i.has(l))break;t=l}i.delete(t);[e,a,r,o,l]=t;if(h!==e){n.push(h,d,e,d===a?a:r);h=e}d=d===a?r:a}n.push(h,d)}return new HighlightOutline(s,this.#mn)}#xn(t){const e=this.#bn;let i=0,s=e.length-1;for(;i<=s;){const n=i+s>>1,a=e[n][0];if(a===t)return n;a<t?i=n+1:s=n-1}return s+1}#vn([,t,e]){const i=this.#xn(t);this.#bn.splice(i,0,[t,e])}#yn([,t,e]){const i=this.#xn(t);for(let s=i;s<this.#bn.length;s++){const[i,n]=this.#bn[s];if(i!==t)break;if(i===t&&n===e){this.#bn.splice(s,1);return}}for(let s=i-1;s>=0;s--){const[i,n]=this.#bn[s];if(i!==t)break;if(i===t&&n===e){this.#bn.splice(s,1);return}}}#An(t){const[e,i,s]=t,n=[[e,i,s]],a=this.#xn(s);for(let t=0;t<a;t++){const[i,s]=this.#bn[t];for(let t=0,a=n.length;t<a;t++){const[,r,o]=n[t];if(!(s<=r||o<=i))if(r>=i)if(o>s)n[t][1]=s;else{if(1===a)return[];n.splice(t,1);t--;a--}else{n[t][2]=i;o>s&&n.push([e,s,o])}}}return n}}class Outline{toSVGPath(){throw new Error("Abstract method `toSVGPath` must be implemented.")}get box(){throw new Error("Abstract getter `box` must be implemented.")}serialize(t,e){throw new Error("Abstract method `serialize` must be implemented.")}get free(){return this instanceof FreeHighlightOutline}}class HighlightOutline extends Outline{#mn;#_n;constructor(t,e){super();this.#_n=t;this.#mn=e}toSVGPath(){const t=[];for(const e of this.#_n){let[i,s]=e;t.push(`M${i} ${s}`);for(let n=2;n<e.length;n+=2){const a=e[n],r=e[n+1];if(a===i){t.push(`V${r}`);s=r}else if(r===s){t.push(`H${a}`);i=a}}t.push("Z")}return t.join(" ")}serialize([t,e,i,s],n){const a=[],r=i-t,o=s-e;for(const e of this.#_n){const i=new Array(e.length);for(let n=0;n<e.length;n+=2){i[n]=t+e[n]*r;i[n+1]=s-e[n+1]*o}a.push(i)}return a}get box(){return this.#mn}}class FreeOutliner{#mn;#En=[];#Cn;#Sn;#Tn=[];#Mn=new Float64Array(18);#kn;#Pn;#Fn;#Dn;#Rn;#In;#Ln=[];static#Nn=8;static#On=2;static#Bn=FreeOutliner.#Nn+FreeOutliner.#On;constructor({x:t,y:e},i,s,n,a,r=0){this.#mn=i;this.#In=n*s;this.#Sn=a;this.#Mn.set([NaN,NaN,NaN,NaN,t,e],6);this.#Cn=r;this.#Dn=FreeOutliner.#Nn*s;this.#Fn=FreeOutliner.#Bn*s;this.#Rn=s;this.#Ln.push(t,e)}get free(){return!0}isEmpty(){return isNaN(this.#Mn[8])}#Hn(){const t=this.#Mn.subarray(4,6),e=this.#Mn.subarray(16,18),[i,s,n,a]=this.#mn;return[(this.#kn+(t[0]-e[0])/2-i)/n,(this.#Pn+(t[1]-e[1])/2-s)/a,(this.#kn+(e[0]-t[0])/2-i)/n,(this.#Pn+(e[1]-t[1])/2-s)/a]}add({x:t,y:e}){this.#kn=t;this.#Pn=e;const[i,s,n,a]=this.#mn;let[r,o,l,h]=this.#Mn.subarray(8,12);const d=t-l,c=e-h,u=Math.hypot(d,c);if(u<this.#Fn)return!1;const p=u-this.#Dn,g=p/u,m=g*d,f=g*c;let b=r,A=o;r=l;o=h;l+=m;h+=f;this.#Ln?.push(t,e);const v=m/p,y=-f/p*this.#In,w=v*this.#In;this.#Mn.set(this.#Mn.subarray(2,8),0);this.#Mn.set([l+y,h+w],4);this.#Mn.set(this.#Mn.subarray(14,18),12);this.#Mn.set([l-y,h-w],16);if(isNaN(this.#Mn[6])){if(0===this.#Tn.length){this.#Mn.set([r+y,o+w],2);this.#Tn.push(NaN,NaN,NaN,NaN,(r+y-i)/n,(o+w-s)/a);this.#Mn.set([r-y,o-w],14);this.#En.push(NaN,NaN,NaN,NaN,(r-y-i)/n,(o-w-s)/a)}this.#Mn.set([b,A,r,o,l,h],6);return!this.isEmpty()}this.#Mn.set([b,A,r,o,l,h],6);if(Math.abs(Math.atan2(A-o,b-r)-Math.atan2(f,m))<Math.PI/2){[r,o,l,h]=this.#Mn.subarray(2,6);this.#Tn.push(NaN,NaN,NaN,NaN,((r+l)/2-i)/n,((o+h)/2-s)/a);[r,o,b,A]=this.#Mn.subarray(14,18);this.#En.push(NaN,NaN,NaN,NaN,((b+r)/2-i)/n,((A+o)/2-s)/a);return!0}[b,A,r,o,l,h]=this.#Mn.subarray(0,6);this.#Tn.push(((b+5*r)/6-i)/n,((A+5*o)/6-s)/a,((5*r+l)/6-i)/n,((5*o+h)/6-s)/a,((r+l)/2-i)/n,((o+h)/2-s)/a);[l,h,r,o,b,A]=this.#Mn.subarray(12,18);this.#En.push(((b+5*r)/6-i)/n,((A+5*o)/6-s)/a,((5*r+l)/6-i)/n,((5*o+h)/6-s)/a,((r+l)/2-i)/n,((o+h)/2-s)/a);return!0}toSVGPath(){if(this.isEmpty())return"";const t=this.#Tn,e=this.#En,i=this.#Mn.subarray(4,6),s=this.#Mn.subarray(16,18),[n,a,r,o]=this.#mn,[l,h,d,c]=this.#Hn();if(isNaN(this.#Mn[6])&&!this.isEmpty())return`M${(this.#Mn[2]-n)/r} ${(this.#Mn[3]-a)/o} L${(this.#Mn[4]-n)/r} ${(this.#Mn[5]-a)/o} L${l} ${h} L${d} ${c} L${(this.#Mn[16]-n)/r} ${(this.#Mn[17]-a)/o} L${(this.#Mn[14]-n)/r} ${(this.#Mn[15]-a)/o} Z`;const u=[];u.push(`M${t[4]} ${t[5]}`);for(let e=6;e<t.length;e+=6)isNaN(t[e])?u.push(`L${t[e+4]} ${t[e+5]}`):u.push(`C${t[e]} ${t[e+1]} ${t[e+2]} ${t[e+3]} ${t[e+4]} ${t[e+5]}`);u.push(`L${(i[0]-n)/r} ${(i[1]-a)/o} L${l} ${h} L${d} ${c} L${(s[0]-n)/r} ${(s[1]-a)/o}`);for(let t=e.length-6;t>=6;t-=6)isNaN(e[t])?u.push(`L${e[t+4]} ${e[t+5]}`):u.push(`C${e[t]} ${e[t+1]} ${e[t+2]} ${e[t+3]} ${e[t+4]} ${e[t+5]}`);u.push(`L${e[4]} ${e[5]} Z`);return u.join(" ")}getOutlines(){const t=this.#Tn,e=this.#En,i=this.#Mn,s=i.subarray(4,6),n=i.subarray(16,18),[a,r,o,l]=this.#mn,h=new Float64Array((this.#Ln?.length??0)+2);for(let t=0,e=h.length-2;t<e;t+=2){h[t]=(this.#Ln[t]-a)/o;h[t+1]=(this.#Ln[t+1]-r)/l}h[h.length-2]=(this.#kn-a)/o;h[h.length-1]=(this.#Pn-r)/l;const[d,c,u,p]=this.#Hn();if(isNaN(i[6])&&!this.isEmpty()){const t=new Float64Array(36);t.set([NaN,NaN,NaN,NaN,(i[2]-a)/o,(i[3]-r)/l,NaN,NaN,NaN,NaN,(i[4]-a)/o,(i[5]-r)/l,NaN,NaN,NaN,NaN,d,c,NaN,NaN,NaN,NaN,u,p,NaN,NaN,NaN,NaN,(i[16]-a)/o,(i[17]-r)/l,NaN,NaN,NaN,NaN,(i[14]-a)/o,(i[15]-r)/l],0);return new FreeHighlightOutline(t,h,this.#mn,this.#Rn,this.#Cn,this.#Sn)}const g=new Float64Array(this.#Tn.length+24+this.#En.length);let m=t.length;for(let e=0;e<m;e+=2)if(isNaN(t[e]))g[e]=g[e+1]=NaN;else{g[e]=t[e];g[e+1]=t[e+1]}g.set([NaN,NaN,NaN,NaN,(s[0]-a)/o,(s[1]-r)/l,NaN,NaN,NaN,NaN,d,c,NaN,NaN,NaN,NaN,u,p,NaN,NaN,NaN,NaN,(n[0]-a)/o,(n[1]-r)/l],m);m+=24;for(let t=e.length-6;t>=6;t-=6)for(let i=0;i<6;i+=2)if(isNaN(e[t+i])){g[m]=g[m+1]=NaN;m+=2}else{g[m]=e[t+i];g[m+1]=e[t+i+1];m+=2}g.set([NaN,NaN,NaN,NaN,e[4],e[5]],m);return new FreeHighlightOutline(g,h,this.#mn,this.#Rn,this.#Cn,this.#Sn)}}class FreeHighlightOutline extends Outline{#mn;#zn=null;#Cn;#Sn;#Ln;#Rn;#Un;constructor(t,e,i,s,n,a){super();this.#Un=t;this.#Ln=e;this.#mn=i;this.#Rn=s;this.#Cn=n;this.#Sn=a;this.#jn(a);const{x:r,y:o,width:l,height:h}=this.#zn;for(let e=0,i=t.length;e<i;e+=2){t[e]=(t[e]-r)/l;t[e+1]=(t[e+1]-o)/h}for(let t=0,i=e.length;t<i;t+=2){e[t]=(e[t]-r)/l;e[t+1]=(e[t+1]-o)/h}}toSVGPath(){const t=[`M${this.#Un[4]} ${this.#Un[5]}`];for(let e=6,i=this.#Un.length;e<i;e+=6)isNaN(this.#Un[e])?t.push(`L${this.#Un[e+4]} ${this.#Un[e+5]}`):t.push(`C${this.#Un[e]} ${this.#Un[e+1]} ${this.#Un[e+2]} ${this.#Un[e+3]} ${this.#Un[e+4]} ${this.#Un[e+5]}`);t.push("Z");return t.join(" ")}serialize([t,e,i,s],n){const a=i-t,r=s-e;let o,l;switch(n){case 0:o=this.#Wn(this.#Un,t,s,a,-r);l=this.#Wn(this.#Ln,t,s,a,-r);break;case 90:o=this.#Gn(this.#Un,t,e,a,r);l=this.#Gn(this.#Ln,t,e,a,r);break;case 180:o=this.#Wn(this.#Un,i,e,-a,r);l=this.#Wn(this.#Ln,i,e,-a,r);break;case 270:o=this.#Gn(this.#Un,i,s,-a,-r);l=this.#Gn(this.#Ln,i,s,-a,-r)}return{outline:Array.from(o),points:[Array.from(l)]}}#Wn(t,e,i,s,n){const a=new Float64Array(t.length);for(let r=0,o=t.length;r<o;r+=2){a[r]=e+t[r]*s;a[r+1]=i+t[r+1]*n}return a}#Gn(t,e,i,s,n){const a=new Float64Array(t.length);for(let r=0,o=t.length;r<o;r+=2){a[r]=e+t[r+1]*s;a[r+1]=i+t[r]*n}return a}#jn(t){const e=this.#Un;let i=e[4],s=e[5],n=i,a=s,r=i,o=s,l=i,h=s;const d=t?Math.max:Math.min;for(let t=6,c=e.length;t<c;t+=6){if(isNaN(e[t])){n=Math.min(n,e[t+4]);a=Math.min(a,e[t+5]);r=Math.max(r,e[t+4]);o=Math.max(o,e[t+5]);if(h<e[t+5]){l=e[t+4];h=e[t+5]}else h===e[t+5]&&(l=d(l,e[t+4]))}else{const c=Util.bezierBoundingBox(i,s,...e.slice(t,t+6));n=Math.min(n,c[0]);a=Math.min(a,c[1]);r=Math.max(r,c[2]);o=Math.max(o,c[3]);if(h<c[3]){l=c[2];h=c[3]}else h===c[3]&&(l=d(l,c[2]))}i=e[t+4];s=e[t+5]}const c=n-this.#Cn,u=a-this.#Cn,p=r-n+2*this.#Cn,g=o-a+2*this.#Cn;this.#zn={x:c,y:u,width:p,height:g,lastPoint:[l,h]}}get box(){return this.#zn}getNewOutline(t,e){const{x:i,y:s,width:n,height:a}=this.#zn,[r,o,l,h]=this.#mn,d=n*l,c=a*h,u=i*l+r,p=s*h+o,g=new FreeOutliner({x:this.#Ln[0]*d+u,y:this.#Ln[1]*c+p},this.#mn,this.#Rn,t,this.#Sn,e??this.#Cn);for(let t=2;t<this.#Ln.length;t+=2)g.add({x:this.#Ln[t]*d+u,y:this.#Ln[t+1]*c+p});return g.getOutlines()}}class ColorPicker{#vs=this.#ys.bind(this);#$n=this.#h.bind(this);#Vn=null;#qn=null;#Xn;#Kn=null;#Yn=!1;#Qn=!1;#a=null;#Jn;#m=null;#Zn;static#ta=null;static get _keyboardManager(){return shadow(this,"_keyboardManager",new KeyboardManager([[["Escape","mac+Escape"],ColorPicker.prototype._hideDropdownFromKeyboard],[[" ","mac+ "],ColorPicker.prototype._colorSelectFromKeyboard],[["ArrowDown","ArrowRight","mac+ArrowDown","mac+ArrowRight"],ColorPicker.prototype._moveToNext],[["ArrowUp","ArrowLeft","mac+ArrowUp","mac+ArrowLeft"],ColorPicker.prototype._moveToPrevious],[["Home","mac+Home"],ColorPicker.prototype._moveToBeginning],[["End","mac+End"],ColorPicker.prototype._moveToEnd]]))}constructor({editor:t=null,uiManager:e=null}){if(t){this.#Qn=!1;this.#Zn=m.HIGHLIGHT_COLOR;this.#a=t}else{this.#Qn=!0;this.#Zn=m.HIGHLIGHT_DEFAULT_COLOR}this.#m=t?._uiManager||e;this.#Jn=this.#m._eventBus;this.#Xn=t?.color||this.#m?.highlightColors.values().next().value||"#FFFF98";ColorPicker.#ta||=Object.freeze({blue:"pdfjs-editor-colorpicker-blue",green:"pdfjs-editor-colorpicker-green",pink:"pdfjs-editor-colorpicker-pink",red:"pdfjs-editor-colorpicker-red",yellow:"pdfjs-editor-colorpicker-yellow"})}renderButton(){const t=this.#Vn=document.createElement("button");t.className="colorPicker";t.tabIndex="0";t.setAttribute("data-l10n-id","pdfjs-editor-colorpicker-button");t.setAttribute("aria-haspopup",!0);const e=this.#m._signal;t.addEventListener("click",this.#ea.bind(this),{signal:e});t.addEventListener("keydown",this.#vs,{signal:e});const i=this.#qn=document.createElement("span");i.className="swatch";i.setAttribute("aria-hidden",!0);i.style.backgroundColor=this.#Xn;t.append(i);return t}renderMainDropdown(){const t=this.#Kn=this.#ia();t.setAttribute("aria-orientation","horizontal");t.setAttribute("aria-labelledby","highlightColorPickerLabel");return t}#ia(){const t=document.createElement("div"),e=this.#m._signal;t.addEventListener("contextmenu",noContextMenu,{signal:e});t.className="dropdown";t.role="listbox";t.setAttribute("aria-multiselectable",!1);t.setAttribute("aria-orientation","vertical");t.setAttribute("data-l10n-id","pdfjs-editor-colorpicker-dropdown");for(const[i,s]of this.#m.highlightColors){const n=document.createElement("button");n.tabIndex="0";n.role="option";n.setAttribute("data-color",s);n.title=i;n.setAttribute("data-l10n-id",ColorPicker.#ta[i]);const a=document.createElement("span");n.append(a);a.className="swatch";a.style.backgroundColor=s;n.setAttribute("aria-selected",s===this.#Xn);n.addEventListener("click",this.#sa.bind(this,s),{signal:e});t.append(n)}t.addEventListener("keydown",this.#vs,{signal:e});return t}#sa(t,e){e.stopPropagation();this.#Jn.dispatch("switchannotationeditorparams",{source:this,type:this.#Zn,value:t})}_colorSelectFromKeyboard(t){if(t.target===this.#Vn){this.#ea(t);return}const e=t.target.getAttribute("data-color");e&&this.#sa(e,t)}_moveToNext(t){this.#na?t.target!==this.#Vn?t.target.nextSibling?.focus():this.#Kn.firstChild?.focus():this.#ea(t)}_moveToPrevious(t){if(t.target!==this.#Kn?.firstChild&&t.target!==this.#Vn){this.#na||this.#ea(t);t.target.previousSibling?.focus()}else this.#na&&this._hideDropdownFromKeyboard()}_moveToBeginning(t){this.#na?this.#Kn.firstChild?.focus():this.#ea(t)}_moveToEnd(t){this.#na?this.#Kn.lastChild?.focus():this.#ea(t)}#ys(t){ColorPicker._keyboardManager.exec(this,t)}#ea(t){if(this.#na){this.hideDropdown();return}this.#Yn=0===t.detail;window.addEventListener("pointerdown",this.#$n,{signal:this.#m._signal});if(this.#Kn){this.#Kn.classList.remove("hidden");return}const e=this.#Kn=this.#ia();this.#Vn.append(e)}#h(t){this.#Kn?.contains(t.target)||this.hideDropdown()}hideDropdown(){this.#Kn?.classList.add("hidden");window.removeEventListener("pointerdown",this.#$n)}get#na(){return this.#Kn&&!this.#Kn.classList.contains("hidden")}_hideDropdownFromKeyboard(){if(!this.#Qn)if(this.#na){this.hideDropdown();this.#Vn.focus({preventScroll:!0,focusVisible:this.#Yn})}else this.#a?.unselect()}updateColor(t){this.#qn&&(this.#qn.style.backgroundColor=t);if(!this.#Kn)return;const e=this.#m.highlightColors.values();for(const i of this.#Kn.children)i.setAttribute("aria-selected",e.next().value===t)}destroy(){this.#Vn?.remove();this.#Vn=null;this.#qn=null;this.#Kn?.remove();this.#Kn=null}}class HighlightEditor extends AnnotationEditor{#aa=null;#ra=0;#oa;#la=null;#n=null;#ha=null;#da=null;#ca=0;#ua=null;#pa=null;#v=null;#ga=!1;#ma=null;#fa;#ba=null;#Aa="";#In;#va="";static _defaultColor=null;static _defaultOpacity=1;static _defaultThickness=12;static _type="highlight";static _editorType=g.HIGHLIGHT;static _freeHighlightId=-1;static _freeHighlight=null;static _freeHighlightClipId="";static get _keyboardManager(){const t=HighlightEditor.prototype;return shadow(this,"_keyboardManager",new KeyboardManager([[["ArrowLeft","mac+ArrowLeft"],t._moveCaret,{args:[0]}],[["ArrowRight","mac+ArrowRight"],t._moveCaret,{args:[1]}],[["ArrowUp","mac+ArrowUp"],t._moveCaret,{args:[2]}],[["ArrowDown","mac+ArrowDown"],t._moveCaret,{args:[3]}]]))}constructor(t){super({...t,name:"highlightEditor"});this.color=t.color||HighlightEditor._defaultColor;this.#In=t.thickness||HighlightEditor._defaultThickness;this.#fa=t.opacity||HighlightEditor._defaultOpacity;this.#oa=t.boxes||null;this.#va=t.methodOfCreation||"";this.#Aa=t.text||"";this._isDraggable=!1;if(t.highlightId>-1){this.#ga=!0;this.#ya(t);this.#wa()}else if(this.#oa){this.#aa=t.anchorNode;this.#ra=t.anchorOffset;this.#da=t.focusNode;this.#ca=t.focusOffset;this.#xa();this.#wa();this.rotate(this.rotation)}}get telemetryInitialData(){return{action:"added",type:this.#ga?"free_highlight":"highlight",color:this._uiManager.highlightColorNames.get(this.color),thickness:this.#In,methodOfCreation:this.#va}}get telemetryFinalData(){return{type:"highlight",color:this._uiManager.highlightColorNames.get(this.color)}}static computeTelemetryFinalData(t){return{numberOfColors:t.get("color").size}}#xa(){const t=new Outliner(this.#oa,.001);this.#pa=t.getOutlines();({x:this.x,y:this.y,width:this.width,height:this.height}=this.#pa.box);const e=new Outliner(this.#oa,.0025,.001,"ltr"===this._uiManager.direction);this.#ha=e.getOutlines();const{lastPoint:i}=this.#ha.box;this.#ma=[(i[0]-this.x)/this.width,(i[1]-this.y)/this.height]}#ya({highlightOutlines:t,highlightId:e,clipPathId:i}){this.#pa=t;this.#ha=t.getNewOutline(this.#In/2+1.5,.0025);if(e>=0){this.#v=e;this.#la=i;this.parent.drawLayer.finalizeLine(e,t);this.#ba=this.parent.drawLayer.highlightOutline(this.#ha)}else if(this.parent){const e=this.parent.viewport.rotation;this.parent.drawLayer.updateLine(this.#v,t);this.parent.drawLayer.updateBox(this.#v,HighlightEditor.#_a(this.#pa.box,(e-this.rotation+360)%360));this.parent.drawLayer.updateLine(this.#ba,this.#ha);this.parent.drawLayer.updateBox(this.#ba,HighlightEditor.#_a(this.#ha.box,e))}const{x:s,y:n,width:a,height:r}=t.box;switch(this.rotation){case 0:this.x=s;this.y=n;this.width=a;this.height=r;break;case 90:{const[t,e]=this.parentDimensions;this.x=n;this.y=1-s;this.width=a*e/t;this.height=r*t/e;break}case 180:this.x=1-s;this.y=1-n;this.width=a;this.height=r;break;case 270:{const[t,e]=this.parentDimensions;this.x=1-n;this.y=s;this.width=a*e/t;this.height=r*t/e;break}}const{lastPoint:o}=this.#ha.box;this.#ma=[(o[0]-s)/a,(o[1]-n)/r]}static initialize(t,e){AnnotationEditor.initialize(t,e);HighlightEditor._defaultColor||=e.highlightColors?.values().next().value||"#fff066"}static updateDefaultParams(t,e){switch(t){case m.HIGHLIGHT_DEFAULT_COLOR:HighlightEditor._defaultColor=e;break;case m.HIGHLIGHT_THICKNESS:HighlightEditor._defaultThickness=e}}translateInPage(t,e){}get toolbarPosition(){return this.#ma}updateParams(t,e){switch(t){case m.HIGHLIGHT_COLOR:this.#on(e);break;case m.HIGHLIGHT_THICKNESS:this.#Ea(e)}}static get defaultPropertiesToUpdate(){return[[m.HIGHLIGHT_DEFAULT_COLOR,HighlightEditor._defaultColor],[m.HIGHLIGHT_THICKNESS,HighlightEditor._defaultThickness]]}get propertiesToUpdate(){return[[m.HIGHLIGHT_COLOR,this.color||HighlightEditor._defaultColor],[m.HIGHLIGHT_THICKNESS,this.#In||HighlightEditor._defaultThickness],[m.HIGHLIGHT_FREE,this.#ga]]}#on(t){const setColorAndOpacity=(t,e)=>{this.color=t;this.parent?.drawLayer.changeColor(this.#v,t);this.#n?.updateColor(t);this.#fa=e;this.parent?.drawLayer.changeOpacity(this.#v,e)},e=this.color,i=this.#fa;this.addCommands({cmd:setColorAndOpacity.bind(this,t,HighlightEditor._defaultOpacity),undo:setColorAndOpacity.bind(this,e,i),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.HIGHLIGHT_COLOR,overwriteIfSameType:!0,keepUndo:!0});this._reportTelemetry({action:"color_changed",color:this._uiManager.highlightColorNames.get(t)},!0)}#Ea(t){const e=this.#In,setThickness=t=>{this.#In=t;this.#Ca(t)};this.addCommands({cmd:setThickness.bind(this,t),undo:setThickness.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.INK_THICKNESS,overwriteIfSameType:!0,keepUndo:!0});this._reportTelemetry({action:"thickness_changed",thickness:t},!0)}async addEditToolbar(){const t=await super.addEditToolbar();if(!t)return null;if(this._uiManager.highlightColors){this.#n=new ColorPicker({editor:this});t.addColorPicker(this.#n)}return t}disableEditing(){super.disableEditing();this.div.classList.toggle("disabled",!0)}enableEditing(){super.enableEditing();this.div.classList.toggle("disabled",!1)}fixAndSetPosition(){return super.fixAndSetPosition(this.#Sa())}getBaseTranslation(){return[0,0]}getRect(t,e){return super.getRect(t,e,this.#Sa())}onceAdded(){this.annotationElementId||this.parent.addUndoableEditor(this);this.div.focus()}remove(){this.#Ta();this._reportTelemetry({action:"deleted"});super.remove()}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){this.#wa();this.isAttachedToDOM||this.parent.add(this)}}}setParent(t){let e=!1;if(this.parent&&!t)this.#Ta();else if(t){this.#wa(t);e=!this.parent&&this.div?.classList.contains("selectedEditor")}super.setParent(t);this.show(this._isVisible);e&&this.select()}#Ca(t){if(!this.#ga)return;this.#ya({highlightOutlines:this.#pa.getNewOutline(t/2)});this.fixAndSetPosition();const[e,i]=this.parentDimensions;this.setDims(this.width*e,this.height*i)}#Ta(){if(null!==this.#v&&this.parent){this.parent.drawLayer.remove(this.#v);this.#v=null;this.parent.drawLayer.remove(this.#ba);this.#ba=null}}#wa(t=this.parent){if(null===this.#v){({id:this.#v,clipPathId:this.#la}=t.drawLayer.highlight(this.#pa,this.color,this.#fa));this.#ba=t.drawLayer.highlightOutline(this.#ha);this.#ua&&(this.#ua.style.clipPath=this.#la)}}static#_a({x:t,y:e,width:i,height:s},n){switch(n){case 90:return{x:1-e-s,y:t,width:s,height:i};case 180:return{x:1-t-i,y:1-e-s,width:i,height:s};case 270:return{x:e,y:1-t-i,width:s,height:i}}return{x:t,y:e,width:i,height:s}}rotate(t){const{drawLayer:e}=this.parent;let i;if(this.#ga){t=(t-this.rotation+360)%360;i=HighlightEditor.#_a(this.#pa.box,t)}else i=HighlightEditor.#_a(this,t);e.rotate(this.#v,t);e.rotate(this.#ba,t);e.updateBox(this.#v,i);e.updateBox(this.#ba,HighlightEditor.#_a(this.#ha.box,t))}render(){if(this.div)return this.div;const t=super.render();if(this.#Aa){t.setAttribute("aria-label",this.#Aa);t.setAttribute("role","mark")}this.#ga?t.classList.add("free"):this.div.addEventListener("keydown",this.#Ma.bind(this),{signal:this._uiManager._signal});const e=this.#ua=document.createElement("div");t.append(e);e.setAttribute("aria-hidden","true");e.className="internal";e.style.clipPath=this.#la;const[i,s]=this.parentDimensions;this.setDims(this.width*i,this.height*s);bindEvents(this,this.#ua,["pointerover","pointerleave"]);this.enableEditing();return t}pointerover(){this.parent.drawLayer.addClass(this.#ba,"hovered")}pointerleave(){this.parent.drawLayer.removeClass(this.#ba,"hovered")}#Ma(t){HighlightEditor._keyboardManager.exec(this,t)}_moveCaret(t){this.parent.unselect(this);switch(t){case 0:case 2:this.#ka(!0);break;case 1:case 3:this.#ka(!1)}}#ka(t){if(!this.#aa)return;const e=window.getSelection();t?e.setPosition(this.#aa,this.#ra):e.setPosition(this.#da,this.#ca)}select(){super.select();if(this.#ba){this.parent?.drawLayer.removeClass(this.#ba,"hovered");this.parent?.drawLayer.addClass(this.#ba,"selected")}}unselect(){super.unselect();if(this.#ba){this.parent?.drawLayer.removeClass(this.#ba,"selected");this.#ga||this.#ka(!1)}}get _mustFixPosition(){return!this.#ga}show(t=this._isVisible){super.show(t);if(this.parent){this.parent.drawLayer.show(this.#v,t);this.parent.drawLayer.show(this.#ba,t)}}#Sa(){return this.#ga?this.rotation:0}#Pa(){if(this.#ga)return null;const[t,e]=this.pageDimensions,[i,s]=this.pageTranslation,n=this.#oa,a=new Float32Array(8*n.length);let r=0;for(const{x:o,y:l,width:h,height:d}of n){const n=o*t+i,c=(1-l-d)*e+s;a[r]=a[r+4]=n;a[r+1]=a[r+3]=c;a[r+2]=a[r+6]=n+h*t;a[r+5]=a[r+7]=c+d*e;r+=8}return a}#Fa(t){return this.#pa.serialize(t,this.#Sa())}static startHighlighting(t,e,{target:i,x:s,y:n}){const{x:a,y:r,width:o,height:l}=i.getBoundingClientRect(),h=new AbortController,d=t.combinedSignal(h),pointerUpCallback=e=>{h.abort();this.#Da(t,e)};window.addEventListener("blur",pointerUpCallback,{signal:d});window.addEventListener("pointerup",pointerUpCallback,{signal:d});window.addEventListener("pointerdown",(t=>{t.preventDefault();t.stopPropagation()}),{capture:!0,passive:!1,signal:d});window.addEventListener("contextmenu",noContextMenu,{signal:d});i.addEventListener("pointermove",this.#Ra.bind(this,t),{signal:d});this._freeHighlight=new FreeOutliner({x:s,y:n},[a,r,o,l],t.scale,this._defaultThickness/2,e,.001);({id:this._freeHighlightId,clipPathId:this._freeHighlightClipId}=t.drawLayer.highlight(this._freeHighlight,this._defaultColor,this._defaultOpacity,!0))}static#Ra(t,e){this._freeHighlight.add(e)&&t.drawLayer.updatePath(this._freeHighlightId,this._freeHighlight)}static#Da(t,e){this._freeHighlight.isEmpty()?t.drawLayer.removeFreeHighlight(this._freeHighlightId):t.createAndAddNewEditor(e,!1,{highlightId:this._freeHighlightId,highlightOutlines:this._freeHighlight.getOutlines(),clipPathId:this._freeHighlightClipId,methodOfCreation:"main_toolbar"});this._freeHighlightId=-1;this._freeHighlight=null;this._freeHighlightClipId=""}static async deserialize(t,e,i){let s=null;if(t instanceof HighlightAnnotationElement){const{data:{quadPoints:e,rect:i,rotation:n,id:a,color:r,opacity:o,popupRef:l},parent:{page:{pageNumber:h}}}=t;s=t={annotationType:g.HIGHLIGHT,color:Array.from(r),opacity:o,quadPoints:e,boxes:null,pageIndex:h-1,rect:i.slice(0),rotation:n,id:a,deleted:!1,popupRef:l}}else if(t instanceof InkAnnotationElement){const{data:{inkLists:e,rect:i,rotation:n,id:a,color:r,borderStyle:{rawWidth:o},popupRef:l},parent:{page:{pageNumber:h}}}=t;s=t={annotationType:g.HIGHLIGHT,color:Array.from(r),thickness:o,inkLists:e,boxes:null,pageIndex:h-1,rect:i.slice(0),rotation:n,id:a,deleted:!1,popupRef:l}}const{color:n,quadPoints:a,inkLists:r,opacity:o}=t,l=await super.deserialize(t,e,i);l.color=Util.makeHexColor(...n);l.#fa=o||1;r&&(l.#In=t.thickness);l.annotationElementId=t.id||null;l._initialData=s;const[h,d]=l.pageDimensions,[c,u]=l.pageTranslation;if(a){const t=l.#oa=[];for(let e=0;e<a.length;e+=8)t.push({x:(a[e]-c)/h,y:1-(a[e+1]-u)/d,width:(a[e+2]-a[e])/h,height:(a[e+1]-a[e+5])/d});l.#xa();l.#wa();l.rotate(l.rotation)}else if(r){l.#ga=!0;const t=r[0],i={x:t[0]-c,y:d-(t[1]-u)},s=new FreeOutliner(i,[0,0,h,d],1,l.#In/2,!0,.001);for(let e=0,n=t.length;e<n;e+=2){i.x=t[e]-c;i.y=d-(t[e+1]-u);s.add(i)}const{id:n,clipPathId:a}=e.drawLayer.highlight(s,l.color,l._defaultOpacity,!0);l.#ya({highlightOutlines:s.getOutlines(),highlightId:n,clipPathId:a});l.#wa()}return l}serialize(t=!1){if(this.isEmpty()||t)return null;if(this.deleted)return this.serializeDeleted();const e=this.getRect(0,0),i=AnnotationEditor._colorManager.convert(this.color),s={annotationType:g.HIGHLIGHT,color:i,opacity:this.#fa,thickness:this.#In,quadPoints:this.#Pa(),outlines:this.#Fa(e),pageIndex:this.pageIndex,rect:e,rotation:this.#Sa(),structTreeParentId:this._structTreeParentId};if(this.annotationElementId&&!this.#gn(s))return null;s.id=this.annotationElementId;return s}#gn(t){const{color:e}=this._initialData;return t.color.some(((t,i)=>t!==e[i]))}renderAnnotationElement(t){t.updateEdited({rect:this.getRect(0,0)});return null}static canCreateNewEmptyEditor(){return!1}}class InkEditor extends AnnotationEditor{#Ia=0;#La=0;#Na=null;#Oa=new Path2D;#Ba=!1;#Ha=null;#za=!1;#Ua=!1;#ja=null;#Wa=null;#Ga=0;#$a=0;#Va=null;static _defaultColor=null;static _defaultOpacity=1;static _defaultThickness=1;static _type="ink";static _editorType=g.INK;constructor(t){super({...t,name:"inkEditor"});this.color=t.color||null;this.thickness=t.thickness||null;this.opacity=t.opacity||null;this.paths=[];this.bezierPath2D=[];this.allRawPaths=[];this.currentPath=[];this.scaleFactor=1;this.translationX=this.translationY=0;this.x=0;this.y=0;this._willKeepAspectRatio=!0}static initialize(t,e){AnnotationEditor.initialize(t,e)}static updateDefaultParams(t,e){switch(t){case m.INK_THICKNESS:InkEditor._defaultThickness=e;break;case m.INK_COLOR:InkEditor._defaultColor=e;break;case m.INK_OPACITY:InkEditor._defaultOpacity=e/100}}updateParams(t,e){switch(t){case m.INK_THICKNESS:this.#Ea(e);break;case m.INK_COLOR:this.#on(e);break;case m.INK_OPACITY:this.#qa(e)}}static get defaultPropertiesToUpdate(){return[[m.INK_THICKNESS,InkEditor._defaultThickness],[m.INK_COLOR,InkEditor._defaultColor||AnnotationEditor._defaultLineColor],[m.INK_OPACITY,Math.round(100*InkEditor._defaultOpacity)]]}get propertiesToUpdate(){return[[m.INK_THICKNESS,this.thickness||InkEditor._defaultThickness],[m.INK_COLOR,this.color||InkEditor._defaultColor||AnnotationEditor._defaultLineColor],[m.INK_OPACITY,Math.round(100*(this.opacity??InkEditor._defaultOpacity))]]}#Ea(t){const setThickness=t=>{this.thickness=t;this.#Xa()},e=this.thickness;this.addCommands({cmd:setThickness.bind(this,t),undo:setThickness.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.INK_THICKNESS,overwriteIfSameType:!0,keepUndo:!0})}#on(t){const setColor=t=>{this.color=t;this.#Ka()},e=this.color;this.addCommands({cmd:setColor.bind(this,t),undo:setColor.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.INK_COLOR,overwriteIfSameType:!0,keepUndo:!0})}#qa(t){const setOpacity=t=>{this.opacity=t;this.#Ka()};t/=100;const e=this.opacity;this.addCommands({cmd:setOpacity.bind(this,t),undo:setOpacity.bind(this,e),post:this._uiManager.updateUI.bind(this._uiManager,this),mustExec:!0,type:m.INK_OPACITY,overwriteIfSameType:!0,keepUndo:!0})}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){if(!this.canvas){this.#Ya();this.#Qa()}if(!this.isAttachedToDOM){this.parent.add(this);this.#Ja()}this.#Xa()}}}remove(){if(null!==this.canvas){this.isEmpty()||this.commit();this.canvas.width=this.canvas.height=0;this.canvas.remove();this.canvas=null;if(this.#Na){clearTimeout(this.#Na);this.#Na=null}this.#ja?.disconnect();this.#ja=null;super.remove()}}setParent(t){!this.parent&&t?this._uiManager.removeShouldRescale(this):this.parent&&null===t&&this._uiManager.addShouldRescale(this);super.setParent(t)}onScaleChanging(){const[t,e]=this.parentDimensions,i=this.width*t,s=this.height*e;this.setDimensions(i,s)}enableEditMode(){if(!this.#Ba&&null!==this.canvas){super.enableEditMode();this._isDraggable=!1;this.#Za()}}disableEditMode(){if(this.isInEditMode()&&null!==this.canvas){super.disableEditMode();this._isDraggable=!this.isEmpty();this.div.classList.remove("editing");this.#tr()}}onceAdded(){this._isDraggable=!this.isEmpty()}isEmpty(){return 0===this.paths.length||1===this.paths.length&&0===this.paths[0].length}#er(){const{parentRotation:t,parentDimensions:[e,i]}=this;switch(t){case 90:return[0,i,i,e];case 180:return[e,i,e,i];case 270:return[e,0,i,e];default:return[0,0,e,i]}}#ir(){const{ctx:t,color:e,opacity:i,thickness:s,parentScale:n,scaleFactor:a}=this;t.lineWidth=s*n/a;t.lineCap="round";t.lineJoin="round";t.miterLimit=10;t.strokeStyle=`${e}${function opacityToHex(t){return Math.round(Math.min(255,Math.max(1,255*t))).toString(16).padStart(2,"0")}(i)}`}#sr(t,e){this.canvas.addEventListener("contextmenu",noContextMenu,{signal:this._uiManager._signal});this.#tr();this.#Ha=new AbortController;const i=this._uiManager.combinedSignal(this.#Ha);this.canvas.addEventListener("pointerleave",this.canvasPointerleave.bind(this),{signal:i});this.canvas.addEventListener("pointermove",this.canvasPointermove.bind(this),{signal:i});this.canvas.addEventListener("pointerup",this.canvasPointerup.bind(this),{signal:i});this.isEditing=!0;if(!this.#Ua){this.#Ua=!0;this.#Ja();this.thickness||=InkEditor._defaultThickness;this.color||=InkEditor._defaultColor||AnnotationEditor._defaultLineColor;this.opacity??=InkEditor._defaultOpacity}this.currentPath.push([t,e]);this.#za=!1;this.#ir();this.#Va=()=>{this.#nr();this.#Va&&window.requestAnimationFrame(this.#Va)};window.requestAnimationFrame(this.#Va)}#ar(t,e){const[i,s]=this.currentPath.at(-1);if(this.currentPath.length>1&&t===i&&e===s)return;const n=this.currentPath;let a=this.#Oa;n.push([t,e]);this.#za=!0;if(n.length<=2){a.moveTo(...n[0]);a.lineTo(t,e)}else{if(3===n.length){this.#Oa=a=new Path2D;a.moveTo(...n[0])}this.#rr(a,...n.at(-3),...n.at(-2),t,e)}}#or(){if(0===this.currentPath.length)return;const t=this.currentPath.at(-1);this.#Oa.lineTo(...t)}#lr(t,e){this.#Va=null;t=Math.min(Math.max(t,0),this.canvas.width);e=Math.min(Math.max(e,0),this.canvas.height);this.#ar(t,e);this.#or();let i;if(1!==this.currentPath.length)i=this.#hr();else{const s=[t,e];i=[[s,s.slice(),s.slice(),s]]}const s=this.#Oa,n=this.currentPath;this.currentPath=[];this.#Oa=new Path2D;this.addCommands({cmd:()=>{this.allRawPaths.push(n);this.paths.push(i);this.bezierPath2D.push(s);this._uiManager.rebuild(this)},undo:()=>{this.allRawPaths.pop();this.paths.pop();this.bezierPath2D.pop();if(0===this.paths.length)this.remove();else{if(!this.canvas){this.#Ya();this.#Qa()}this.#Xa()}},mustExec:!0})}#nr(){if(!this.#za)return;this.#za=!1;const t=Math.ceil(this.thickness*this.parentScale),e=this.currentPath.slice(-3),i=e.map((t=>t[0])),s=e.map((t=>t[1])),{ctx:n}=(Math.min(...i),Math.max(...i),Math.min(...s),Math.max(...s),this);n.save();n.clearRect(0,0,this.canvas.width,this.canvas.height);for(const t of this.bezierPath2D)n.stroke(t);n.stroke(this.#Oa);n.restore()}#rr(t,e,i,s,n,a,r){const o=(e+s)/2,l=(i+n)/2,h=(s+a)/2,d=(n+r)/2;t.bezierCurveTo(o+2*(s-o)/3,l+2*(n-l)/3,h+2*(s-h)/3,d+2*(n-d)/3,h,d)}#hr(){const t=this.currentPath;if(t.length<=2)return[[t[0],t[0],t.at(-1),t.at(-1)]];const e=[];let i,[s,n]=t[0];for(i=1;i<t.length-2;i++){const[a,r]=t[i],[o,l]=t[i+1],h=(a+o)/2,d=(r+l)/2,c=[s+2*(a-s)/3,n+2*(r-n)/3],u=[h+2*(a-h)/3,d+2*(r-d)/3];e.push([[s,n],c,u,[h,d]]);[s,n]=[h,d]}const[a,r]=t[i],[o,l]=t[i+1],h=[s+2*(a-s)/3,n+2*(r-n)/3],d=[o+2*(a-o)/3,l+2*(r-l)/3];e.push([[s,n],h,d,[o,l]]);return e}#Ka(){if(this.isEmpty()){this.#dr();return}this.#ir();const{canvas:t,ctx:e}=this;e.setTransform(1,0,0,1,0,0);e.clearRect(0,0,t.width,t.height);this.#dr();for(const t of this.bezierPath2D)e.stroke(t)}commit(){if(!this.#Ba){super.commit();this.isEditing=!1;this.disableEditMode();this.setInForeground();this.#Ba=!0;this.div.classList.add("disabled");this.#Xa(!0);this.select();this.parent.addInkEditorIfNeeded(!0);this.moveInDOM();this.div.focus({preventScroll:!0})}}focusin(t){if(this._focusEventsAllowed){super.focusin(t);this.enableEditMode()}}#Za(){if(this.#Wa)return;this.#Wa=new AbortController;const t=this._uiManager.combinedSignal(this.#Wa);this.canvas.addEventListener("pointerdown",this.canvasPointerdown.bind(this),{signal:t})}#tr(){this.pointerdownAC?.abort();this.pointerdownAC=null}canvasPointerdown(t){if(0===t.button&&this.isInEditMode()&&!this.#Ba){this.setInForeground();t.preventDefault();this.div.contains(document.activeElement)||this.div.focus({preventScroll:!0});this.#sr(t.offsetX,t.offsetY)}}canvasPointermove(t){t.preventDefault();this.#ar(t.offsetX,t.offsetY)}canvasPointerup(t){t.preventDefault();this.#cr(t)}canvasPointerleave(t){this.#cr(t)}#cr(t){this.#Ha?.abort();this.#Ha=null;this.#Za();this.#Na&&clearTimeout(this.#Na);this.#Na=setTimeout((()=>{this.#Na=null;this.canvas.removeEventListener("contextmenu",noContextMenu)}),10);this.#lr(t.offsetX,t.offsetY);this.addToAnnotationStorage();this.setInBackground()}#Ya(){this.canvas=document.createElement("canvas");this.canvas.width=this.canvas.height=0;this.canvas.className="inkEditorCanvas";this.canvas.setAttribute("data-l10n-id","pdfjs-ink-canvas");this.div.append(this.canvas);this.ctx=this.canvas.getContext("2d")}#Qa(){this.#ja=new ResizeObserver((t=>{const e=t[0].contentRect;e.width&&e.height&&this.setDimensions(e.width,e.height)}));this.#ja.observe(this.div);this._uiManager._signal.addEventListener("abort",(()=>{this.#ja?.disconnect();this.#ja=null}),{once:!0})}get isResizable(){return!this.isEmpty()&&this.#Ba}render(){if(this.div)return this.div;let t,e;if(this.width){t=this.x;e=this.y}super.render();this.div.setAttribute("data-l10n-id","pdfjs-ink");const[i,s,n,a]=this.#er();this.setAt(i,s,0,0);this.setDims(n,a);this.#Ya();if(this.width){const[i,s]=this.parentDimensions;this.setAspectRatio(this.width*i,this.height*s);this.setAt(t*i,e*s,this.width*i,this.height*s);this.#Ua=!0;this.#Ja();this.setDims(this.width*i,this.height*s);this.#Ka();this.div.classList.add("disabled")}else{this.div.classList.add("editing");this.enableEditMode()}this.#Qa();return this.div}#Ja(){if(!this.#Ua)return;const[t,e]=this.parentDimensions;this.canvas.width=Math.ceil(this.width*t);this.canvas.height=Math.ceil(this.height*e);this.#dr()}setDimensions(t,e){const i=Math.round(t),s=Math.round(e);if(this.#Ga===i&&this.#$a===s)return;this.#Ga=i;this.#$a=s;this.canvas.style.visibility="hidden";const[n,a]=this.parentDimensions;this.width=t/n;this.height=e/a;this.fixAndSetPosition();this.#Ba&&this.#ur(t,e);this.#Ja();this.#Ka();this.canvas.style.visibility="visible";this.fixDims()}#ur(t,e){const i=this.#pr(),s=(t-i)/this.#La,n=(e-i)/this.#Ia;this.scaleFactor=Math.min(s,n)}#dr(){const t=this.#pr()/2;this.ctx.setTransform(this.scaleFactor,0,0,this.scaleFactor,this.translationX*this.scaleFactor+t,this.translationY*this.scaleFactor+t)}static#gr(t){const e=new Path2D;for(let i=0,s=t.length;i<s;i++){const[s,n,a,r]=t[i];0===i&&e.moveTo(...s);e.bezierCurveTo(n[0],n[1],a[0],a[1],r[0],r[1])}return e}static#mr(t,e,i){const[s,n,a,r]=e;switch(i){case 0:for(let e=0,i=t.length;e<i;e+=2){t[e]+=s;t[e+1]=r-t[e+1]}break;case 90:for(let e=0,i=t.length;e<i;e+=2){const i=t[e];t[e]=t[e+1]+s;t[e+1]=i+n}break;case 180:for(let e=0,i=t.length;e<i;e+=2){t[e]=a-t[e];t[e+1]+=n}break;case 270:for(let e=0,i=t.length;e<i;e+=2){const i=t[e];t[e]=a-t[e+1];t[e+1]=r-i}break;default:throw new Error("Invalid rotation")}return t}static#fr(t,e,i){const[s,n,a,r]=e;switch(i){case 0:for(let e=0,i=t.length;e<i;e+=2){t[e]-=s;t[e+1]=r-t[e+1]}break;case 90:for(let e=0,i=t.length;e<i;e+=2){const i=t[e];t[e]=t[e+1]-n;t[e+1]=i-s}break;case 180:for(let e=0,i=t.length;e<i;e+=2){t[e]=a-t[e];t[e+1]-=n}break;case 270:for(let e=0,i=t.length;e<i;e+=2){const i=t[e];t[e]=r-t[e+1];t[e+1]=a-i}break;default:throw new Error("Invalid rotation")}return t}#br(t,e,i,s){const n=[],a=this.thickness/2,r=t*e+a,o=t*i+a;for(const e of this.paths){const i=[],a=[];for(let s=0,n=e.length;s<n;s++){const[l,h,d,c]=e[s];if(l[0]===c[0]&&l[1]===c[1]&&1===n){const e=t*l[0]+r,s=t*l[1]+o;i.push(e,s);a.push(e,s);break}const u=t*l[0]+r,p=t*l[1]+o,g=t*h[0]+r,m=t*h[1]+o,f=t*d[0]+r,b=t*d[1]+o,A=t*c[0]+r,v=t*c[1]+o;if(0===s){i.push(u,p);a.push(u,p)}i.push(g,m,f,b,A,v);a.push(g,m);s===n-1&&a.push(A,v)}n.push({bezier:InkEditor.#mr(i,s,this.rotation),points:InkEditor.#mr(a,s,this.rotation)})}return n}#Ar(){let t=1/0,e=-1/0,i=1/0,s=-1/0;for(const n of this.paths)for(const[a,r,o,l]of n){const n=Util.bezierBoundingBox(...a,...r,...o,...l);t=Math.min(t,n[0]);i=Math.min(i,n[1]);e=Math.max(e,n[2]);s=Math.max(s,n[3])}return[t,i,e,s]}#pr(){return this.#Ba?Math.ceil(this.thickness*this.parentScale):0}#Xa(t=!1){if(this.isEmpty())return;if(!this.#Ba){this.#Ka();return}const e=this.#Ar(),i=this.#pr();this.#La=Math.max(AnnotationEditor.MIN_SIZE,e[2]-e[0]);this.#Ia=Math.max(AnnotationEditor.MIN_SIZE,e[3]-e[1]);const s=Math.ceil(i+this.#La*this.scaleFactor),n=Math.ceil(i+this.#Ia*this.scaleFactor),[a,r]=this.parentDimensions;this.width=s/a;this.height=n/r;this.setAspectRatio(s,n);const o=this.translationX,l=this.translationY;this.translationX=-e[0];this.translationY=-e[1];this.#Ja();this.#Ka();this.#Ga=s;this.#$a=n;this.setDims(s,n);const h=t?i/this.scaleFactor/2:0;this.translate(o-this.translationX-h,l-this.translationY-h)}static async deserialize(t,e,i){if(t instanceof InkAnnotationElement)return null;const s=await super.deserialize(t,e,i);s.thickness=t.thickness;s.color=Util.makeHexColor(...t.color);s.opacity=t.opacity;const[n,a]=s.pageDimensions,r=s.width*n,o=s.height*a,l=s.parentScale,h=t.thickness/2;s.#Ba=!0;s.#Ga=Math.round(r);s.#$a=Math.round(o);const{paths:d,rect:c,rotation:u}=t;for(let{bezier:t}of d){t=InkEditor.#fr(t,c,u);const e=[];s.paths.push(e);let i=l*(t[0]-h),n=l*(t[1]-h);for(let s=2,a=t.length;s<a;s+=6){const a=l*(t[s]-h),r=l*(t[s+1]-h),o=l*(t[s+2]-h),d=l*(t[s+3]-h),c=l*(t[s+4]-h),u=l*(t[s+5]-h);e.push([[i,n],[a,r],[o,d],[c,u]]);i=c;n=u}const a=this.#gr(e);s.bezierPath2D.push(a)}const p=s.#Ar();s.#La=Math.max(AnnotationEditor.MIN_SIZE,p[2]-p[0]);s.#Ia=Math.max(AnnotationEditor.MIN_SIZE,p[3]-p[1]);s.#ur(r,o);return s}serialize(){if(this.isEmpty())return null;const t=this.getRect(0,0),e=AnnotationEditor._colorManager.convert(this.ctx.strokeStyle);return{annotationType:g.INK,color:e,thickness:this.thickness,opacity:this.opacity,paths:this.#br(this.scaleFactor/this.parentScale,this.translationX,this.translationY,t),pageIndex:this.pageIndex,rect:t,rotation:this.rotation,structTreeParentId:this._structTreeParentId}}}class StampEditor extends AnnotationEditor{#vr=null;#yr=null;#wr=null;#xr=null;#_r=null;#Er="";#Cr=null;#ja=null;#Sr=null;#Tr=!1;#Mr=!1;static _type="stamp";static _editorType=g.STAMP;constructor(t){super({...t,name:"stampEditor"});this.#xr=t.bitmapUrl;this.#_r=t.bitmapFile}static initialize(t,e){AnnotationEditor.initialize(t,e)}static get supportedTypes(){return shadow(this,"supportedTypes",["apng","avif","bmp","gif","jpeg","png","svg+xml","webp","x-icon"].map((t=>`image/${t}`)))}static get supportedTypesStr(){return shadow(this,"supportedTypesStr",this.supportedTypes.join(","))}static isHandlingMimeForPasting(t){return this.supportedTypes.includes(t)}static paste(t,e){e.pasteEditor(g.STAMP,{bitmapFile:t.getAsFile()})}altTextFinish(){this._uiManager.useNewAltTextFlow&&(this.div.hidden=!1);super.altTextFinish()}get telemetryFinalData(){return{type:"stamp",hasAltText:!!this.altTextData?.altText}}static computeTelemetryFinalData(t){const e=t.get("hasAltText");return{hasAltText:e.get(!0)??0,hasNoAltText:e.get(!1)??0}}#kr(t,e=!1){if(t){this.#vr=t.bitmap;if(!e){this.#yr=t.id;this.#Tr=t.isSvg}t.file&&(this.#Er=t.file.name);this.#Ya()}else this.remove()}#Pr(){this.#wr=null;this._uiManager.enableWaiting(!1);if(this.#Cr)if(this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&this.#vr){this._editToolbar.hide();this._uiManager.editAltText(this,!0)}else{if(!this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&this.#vr){this._reportTelemetry({action:"pdfjs.image.image_added",data:{alt_text_modal:!1,alt_text_type:"empty"}});try{this.mlGuessAltText()}catch{}}this.div.focus()}}async mlGuessAltText(t=null,e=!0){if(this.hasAltTextData())return null;const{mlManager:i}=this._uiManager;if(!i)throw new Error("No ML.");if(!await i.isEnabledFor("altText"))throw new Error("ML isn't enabled for alt text.");const{data:s,width:n,height:a}=t||this.copyCanvas(null,null,!0).imageData,r=await i.guess({name:"altText",request:{data:s,width:n,height:a,channels:s.length/(n*a)}});if(!r)throw new Error("No response from the AI service.");if(r.error)throw new Error("Error from the AI service.");if(r.cancel)return null;if(!r.output)throw new Error("No valid response from the AI service.");const o=r.output;await this.setGuessedAltText(o);e&&!this.hasAltTextData()&&(this.altTextData={alt:o,decorative:!1});return o}#Fr(){if(this.#yr){this._uiManager.enableWaiting(!0);this._uiManager.imageManager.getFromId(this.#yr).then((t=>this.#kr(t,!0))).finally((()=>this.#Pr()));return}if(this.#xr){const t=this.#xr;this.#xr=null;this._uiManager.enableWaiting(!0);this.#wr=this._uiManager.imageManager.getFromUrl(t).then((t=>this.#kr(t))).finally((()=>this.#Pr()));return}if(this.#_r){const t=this.#_r;this.#_r=null;this._uiManager.enableWaiting(!0);this.#wr=this._uiManager.imageManager.getFromFile(t).then((t=>this.#kr(t))).finally((()=>this.#Pr()));return}const t=document.createElement("input");t.type="file";t.accept=StampEditor.supportedTypesStr;const e=this._uiManager._signal;this.#wr=new Promise((i=>{t.addEventListener("change",(async()=>{if(t.files&&0!==t.files.length){this._uiManager.enableWaiting(!0);const e=await this._uiManager.imageManager.getFromFile(t.files[0]);this._reportTelemetry({action:"pdfjs.image.image_selected",data:{alt_text_modal:this._uiManager.useNewAltTextFlow}});this.#kr(e)}else this.remove();i()}),{signal:e});t.addEventListener("cancel",(()=>{this.remove();i()}),{signal:e})})).finally((()=>this.#Pr()));t.click()}remove(){if(this.#yr){this.#vr=null;this._uiManager.imageManager.deleteId(this.#yr);this.#Cr?.remove();this.#Cr=null;this.#ja?.disconnect();this.#ja=null;if(this.#Sr){clearTimeout(this.#Sr);this.#Sr=null}}super.remove()}rebuild(){if(this.parent){super.rebuild();if(null!==this.div){this.#yr&&null===this.#Cr&&this.#Fr();this.isAttachedToDOM||this.parent.add(this)}}else this.#yr&&this.#Fr()}onceAdded(){this._isDraggable=!0;this.div.focus()}isEmpty(){return!(this.#wr||this.#vr||this.#xr||this.#_r||this.#yr)}get isResizable(){return!0}render(){if(this.div)return this.div;let t,e;if(this.width){t=this.x;e=this.y}super.render();this.div.hidden=!0;this.div.setAttribute("role","figure");this.addAltTextButton();this.#vr?this.#Ya():this.#Fr();if(this.width&&!this.annotationElementId){const[i,s]=this.parentDimensions;this.setAt(t*i,e*s,this.width*i,this.height*s)}return this.div}#Ya(){const{div:t}=this;let{width:e,height:i}=this.#vr;const[s,n]=this.pageDimensions,a=.75;if(this.width){e=this.width*s;i=this.height*n}else if(e>a*s||i>a*n){const t=Math.min(a*s/e,a*n/i);e*=t;i*=t}const[r,o]=this.parentDimensions;this.setDims(e*r/s,i*o/n);this._uiManager.enableWaiting(!1);const l=this.#Cr=document.createElement("canvas");l.setAttribute("role","img");this.addContainer(l);this._uiManager.useNewAltTextWhenAddingImage&&this._uiManager.useNewAltTextFlow&&!this.annotationElementId||(t.hidden=!1);this.#Dr(e,i);this.#Qa();if(!this.#Mr){this.parent.addUndoableEditor(this);this.#Mr=!0}this._reportTelemetry({action:"inserted_image"});this.#Er&&l.setAttribute("aria-label",this.#Er)}copyCanvas(t,e,i=!1){t||(t=224);const{width:s,height:n}=this.#vr,a=new OutputScale;let r=this.#vr,o=s,l=n,h=null;if(e){if(s>e||n>e){const t=Math.min(e/s,e/n);o=Math.floor(s*t);l=Math.floor(n*t)}h=document.createElement("canvas");const t=h.width=Math.ceil(o*a.sx),i=h.height=Math.ceil(l*a.sy);this.#Tr||(r=this.#Rr(t,i));const d=h.getContext("2d");d.filter=this._uiManager.hcmFilter;let c="white",u="#cfcfd8";if("none"!==this._uiManager.hcmFilter)u="black";else if(window.matchMedia?.("(prefers-color-scheme: dark)").matches){c="#8f8f9d";u="#42414d"}const p=15,g=p*a.sx,m=p*a.sy,f=new OffscreenCanvas(2*g,2*m),b=f.getContext("2d");b.fillStyle=c;b.fillRect(0,0,2*g,2*m);b.fillStyle=u;b.fillRect(0,0,g,m);b.fillRect(g,m,g,m);d.fillStyle=d.createPattern(f,"repeat");d.fillRect(0,0,t,i);d.drawImage(r,0,0,r.width,r.height,0,0,t,i)}let d=null;if(i){let e,i;if(a.symmetric&&r.width<t&&r.height<t){e=r.width;i=r.height}else{r=this.#vr;if(s>t||n>t){const a=Math.min(t/s,t/n);e=Math.floor(s*a);i=Math.floor(n*a);this.#Tr||(r=this.#Rr(e,i))}}const o=new OffscreenCanvas(e,i).getContext("2d",{willReadFrequently:!0});o.drawImage(r,0,0,r.width,r.height,0,0,e,i);d={width:e,height:i,data:o.getImageData(0,0,e,i).data}}return{canvas:h,width:o,height:l,imageData:d}}#Ir(t,e){const[i,s]=this.parentDimensions;this.width=t/i;this.height=e/s;this._initialOptions?.isCentered?this.center():this.fixAndSetPosition();this._initialOptions=null;null!==this.#Sr&&clearTimeout(this.#Sr);this.#Sr=setTimeout((()=>{this.#Sr=null;this.#Dr(t,e)}),200)}#Rr(t,e){const{width:i,height:s}=this.#vr;let n=i,a=s,r=this.#vr;for(;n>2*t||a>2*e;){const i=n,s=a;n>2*t&&(n=n>=16384?Math.floor(n/2)-1:Math.ceil(n/2));a>2*e&&(a=a>=16384?Math.floor(a/2)-1:Math.ceil(a/2));const o=new OffscreenCanvas(n,a);o.getContext("2d").drawImage(r,0,0,i,s,0,0,n,a);r=o.transferToImageBitmap()}return r}#Dr(t,e){const i=new OutputScale,s=Math.ceil(t*i.sx),n=Math.ceil(e*i.sy),a=this.#Cr;if(!a||a.width===s&&a.height===n)return;a.width=s;a.height=n;const r=this.#Tr?this.#vr:this.#Rr(s,n),o=a.getContext("2d");o.filter=this._uiManager.hcmFilter;o.drawImage(r,0,0,r.width,r.height,0,0,s,n)}getImageForAltText(){return this.#Cr}#Lr(t){if(t){if(this.#Tr){const t=this._uiManager.imageManager.getSvgUrl(this.#yr);if(t)return t}const t=document.createElement("canvas");({width:t.width,height:t.height}=this.#vr);t.getContext("2d").drawImage(this.#vr,0,0);return t.toDataURL()}if(this.#Tr){const[t,e]=this.pageDimensions,i=Math.round(this.width*t*PixelsPerInch.PDF_TO_CSS_UNITS),s=Math.round(this.height*e*PixelsPerInch.PDF_TO_CSS_UNITS),n=new OffscreenCanvas(i,s);n.getContext("2d").drawImage(this.#vr,0,0,this.#vr.width,this.#vr.height,0,0,i,s);return n.transferToImageBitmap()}return structuredClone(this.#vr)}#Qa(){if(this._uiManager._signal){this.#ja=new ResizeObserver((t=>{const e=t[0].contentRect;e.width&&e.height&&this.#Ir(e.width,e.height)}));this.#ja.observe(this.div);this._uiManager._signal.addEventListener("abort",(()=>{this.#ja?.disconnect();this.#ja=null}),{once:!0})}}static async deserialize(t,e,i){let s=null;if(t instanceof StampAnnotationElement){const{data:{rect:n,rotation:a,id:r,structParent:o,popupRef:l},container:h,parent:{page:{pageNumber:d}}}=t,c=h.querySelector("canvas"),u=i.imageManager.getFromCanvas(h.id,c);c.remove();const p=(await e._structTree.getAriaAttributes(`${it}${r}`))?.get("aria-label")||"";s=t={annotationType:g.STAMP,bitmapId:u.id,bitmap:u.bitmap,pageIndex:d-1,rect:n.slice(0),rotation:a,id:r,deleted:!1,accessibilityData:{decorative:!1,altText:p},isSvg:!1,structParent:o,popupRef:l}}const n=await super.deserialize(t,e,i),{rect:a,bitmap:r,bitmapUrl:o,bitmapId:l,isSvg:h,accessibilityData:d}=t;if(l&&i.imageManager.isValidId(l)){n.#yr=l;r&&(n.#vr=r)}else n.#xr=o;n.#Tr=h;const[c,u]=n.pageDimensions;n.width=(a[2]-a[0])/c;n.height=(a[3]-a[1])/u;n.annotationElementId=t.id||null;d&&(n.altTextData=d);n._initialData=s;n.#Mr=!!s;return n}serialize(t=!1,e=null){if(this.isEmpty())return null;if(this.deleted)return this.serializeDeleted();const i={annotationType:g.STAMP,bitmapId:this.#yr,pageIndex:this.pageIndex,rect:this.getRect(0,0),rotation:this.rotation,isSvg:this.#Tr,structTreeParentId:this._structTreeParentId};if(t){i.bitmapUrl=this.#Lr(!0);i.accessibilityData=this.serializeAltText(!0);return i}const{decorative:s,altText:n}=this.serializeAltText(!1);!s&&n&&(i.accessibilityData={type:"Figure",alt:n});if(this.annotationElementId){const t=this.#gn(i);if(t.isSame)return null;t.isSameAltText?delete i.accessibilityData:i.accessibilityData.structParent=this._initialData.structParent??-1}i.id=this.annotationElementId;if(null===e)return i;e.stamps||=new Map;const a=this.#Tr?(i.rect[2]-i.rect[0])*(i.rect[3]-i.rect[1]):null;if(e.stamps.has(this.#yr)){if(this.#Tr){const t=e.stamps.get(this.#yr);if(a>t.area){t.area=a;t.serialized.bitmap.close();t.serialized.bitmap=this.#Lr(!1)}}}else{e.stamps.set(this.#yr,{area:a,serialized:i});i.bitmap=this.#Lr(!1)}return i}#gn(t){const{rect:e,pageIndex:i,accessibilityData:{altText:s}}=this._initialData,n=t.rect.every(((t,i)=>Math.abs(t-e[i])<1)),a=t.pageIndex===i,r=(t.accessibilityData?.alt||"")===s;return{isSame:n&&a&&r,isSameAltText:r}}renderAnnotationElement(t){t.updateEdited({rect:this.getRect(0,0)});return null}}class AnnotationEditorLayer{#Qs;#Nr=!1;#Or=null;#Br=null;#Hr=null;#zr=new Map;#Ur=!1;#jr=!1;#Wr=!1;#Gr=null;#$r=null;#m;static _initialized=!1;static#z=new Map([FreeTextEditor,InkEditor,StampEditor,HighlightEditor].map((t=>[t._editorType,t])));constructor({uiManager:t,pageIndex:e,div:i,structTreeLayer:s,accessibilityManager:n,annotationLayer:a,drawLayer:r,textLayer:o,viewport:l,l10n:h}){const d=[...AnnotationEditorLayer.#z.values()];if(!AnnotationEditorLayer._initialized){AnnotationEditorLayer._initialized=!0;for(const e of d)e.initialize(h,t)}t.registerEditorTypes(d);this.#m=t;this.pageIndex=e;this.div=i;this.#Qs=n;this.#Or=a;this.viewport=l;this.#Gr=o;this.drawLayer=r;this._structTree=s;this.#m.addLayer(this)}get isEmpty(){return 0===this.#zr.size}get isInvisible(){return this.isEmpty&&this.#m.getMode()===g.NONE}updateToolbar(t){this.#m.updateToolbar(t)}updateMode(t=this.#m.getMode()){this.#Vr();switch(t){case g.NONE:this.disableTextSelection();this.togglePointerEvents(!1);this.toggleAnnotationLayerPointerEvents(!0);this.disableClick();return;case g.INK:this.addInkEditorIfNeeded(!1);this.disableTextSelection();this.togglePointerEvents(!0);this.disableClick();break;case g.HIGHLIGHT:this.enableTextSelection();this.togglePointerEvents(!1);this.disableClick();break;default:this.disableTextSelection();this.togglePointerEvents(!0);this.enableClick()}this.toggleAnnotationLayerPointerEvents(!1);const{classList:e}=this.div;for(const i of AnnotationEditorLayer.#z.values())e.toggle(`${i._type}Editing`,t===i._editorType);this.div.hidden=!1}hasTextLayer(t){return t===this.#Gr?.div}addInkEditorIfNeeded(t){if(this.#m.getMode()!==g.INK)return;if(!t)for(const t of this.#zr.values())if(t.isEmpty()){t.setInBackground();return}this.createAndAddNewEditor({offsetX:0,offsetY:0},!1).setInBackground()}setEditingState(t){this.#m.setEditingState(t)}addCommands(t){this.#m.addCommands(t)}toggleDrawing(t=!1){this.div.classList.toggle("drawing",!t)}togglePointerEvents(t=!1){this.div.classList.toggle("disabled",!t)}toggleAnnotationLayerPointerEvents(t=!1){this.#Or?.div.classList.toggle("disabled",!t)}async enable(){this.div.tabIndex=0;this.togglePointerEvents(!0);const t=new Set;for(const e of this.#zr.values()){e.enableEditing();e.show(!0);if(e.annotationElementId){this.#m.removeChangedExistingAnnotation(e);t.add(e.annotationElementId)}}if(!this.#Or)return;const e=this.#Or.getEditableAnnotations();for(const i of e){i.hide();if(this.#m.isDeletedAnnotationElement(i.data.id))continue;if(t.has(i.data.id))continue;const e=await this.deserialize(i);if(e){this.addOrRebuild(e);e.enableEditing()}}}disable(){this.#Wr=!0;this.div.tabIndex=-1;this.togglePointerEvents(!1);const t=new Map,e=new Map;for(const i of this.#zr.values()){i.disableEditing();if(i.annotationElementId)if(null===i.serialize()){e.set(i.annotationElementId,i);this.getEditableAnnotation(i.annotationElementId)?.show();i.remove()}else t.set(i.annotationElementId,i)}if(this.#Or){const i=this.#Or.getEditableAnnotations();for(const s of i){const{id:i}=s.data;if(this.#m.isDeletedAnnotationElement(i))continue;let n=e.get(i);if(n){n.resetAnnotationElement(s);n.show(!1);s.show()}else{n=t.get(i);if(n){this.#m.addChangedExistingAnnotation(n);n.renderAnnotationElement(s)&&n.show(!1)}s.show()}}}this.#Vr();this.isEmpty&&(this.div.hidden=!0);const{classList:i}=this.div;for(const t of AnnotationEditorLayer.#z.values())i.remove(`${t._type}Editing`);this.disableTextSelection();this.toggleAnnotationLayerPointerEvents(!0);this.#Wr=!1}getEditableAnnotation(t){return this.#Or?.getEditableAnnotation(t)||null}setActiveEditor(t){this.#m.getActive()!==t&&this.#m.setActiveEditor(t)}enableTextSelection(){this.div.tabIndex=-1;if(this.#Gr?.div&&!this.#$r){this.#$r=new AbortController;const t=this.#m.combinedSignal(this.#$r);this.#Gr.div.addEventListener("pointerdown",this.#qr.bind(this),{signal:t});this.#Gr.div.classList.add("highlighting")}}disableTextSelection(){this.div.tabIndex=0;if(this.#Gr?.div&&this.#$r){this.#$r.abort();this.#$r=null;this.#Gr.div.classList.remove("highlighting")}}#qr(t){this.#m.unselectAll();const{target:e}=t;if(e===this.#Gr.div||("img"===e.getAttribute("role")||e.classList.contains("endOfContent"))&&this.#Gr.div.contains(e)){const{isMac:e}=util_FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)return;this.#m.showAllEditors("highlight",!0,!0);this.#Gr.div.classList.add("free");this.toggleDrawing();HighlightEditor.startHighlighting(this,"ltr"===this.#m.direction,{target:this.#Gr.div,x:t.x,y:t.y});this.#Gr.div.addEventListener("pointerup",(()=>{this.#Gr.div.classList.remove("free");this.toggleDrawing(!0)}),{once:!0,signal:this.#m._signal});t.preventDefault()}}enableClick(){if(this.#Br)return;this.#Br=new AbortController;const t=this.#m.combinedSignal(this.#Br);this.div.addEventListener("pointerdown",this.pointerdown.bind(this),{signal:t});this.div.addEventListener("pointerup",this.pointerup.bind(this),{signal:t})}disableClick(){this.#Br?.abort();this.#Br=null}attach(t){this.#zr.set(t.id,t);const{annotationElementId:e}=t;e&&this.#m.isDeletedAnnotationElement(e)&&this.#m.removeDeletedAnnotationElement(t)}detach(t){this.#zr.delete(t.id);this.#Qs?.removePointerInTextLayer(t.contentDiv);!this.#Wr&&t.annotationElementId&&this.#m.addDeletedAnnotationElement(t)}remove(t){this.detach(t);this.#m.removeEditor(t);t.div.remove();t.isAttachedToDOM=!1;this.#jr||this.addInkEditorIfNeeded(!1)}changeParent(t){if(t.parent!==this){if(t.parent&&t.annotationElementId){this.#m.addDeletedAnnotationElement(t.annotationElementId);AnnotationEditor.deleteAnnotationElement(t);t.annotationElementId=null}this.attach(t);t.parent?.detach(t);t.setParent(this);if(t.div&&t.isAttachedToDOM){t.div.remove();this.div.append(t.div)}}}add(t){if(t.parent!==this||!t.isAttachedToDOM){this.changeParent(t);this.#m.addEditor(t);this.attach(t);if(!t.isAttachedToDOM){const e=t.render();this.div.append(e);t.isAttachedToDOM=!0}t.fixAndSetPosition();t.onceAdded();this.#m.addToAnnotationStorage(t);t._reportTelemetry(t.telemetryInitialData)}}moveEditorInDOM(t){if(!t.isAttachedToDOM)return;const{activeElement:e}=document;if(t.div.contains(e)&&!this.#Hr){t._focusEventsAllowed=!1;this.#Hr=setTimeout((()=>{this.#Hr=null;if(t.div.contains(document.activeElement))t._focusEventsAllowed=!0;else{t.div.addEventListener("focusin",(()=>{t._focusEventsAllowed=!0}),{once:!0,signal:this.#m._signal});e.focus()}}),0)}t._structTreeParentId=this.#Qs?.moveElementInDOM(this.div,t.div,t.contentDiv,!0)}addOrRebuild(t){if(t.needsToBeRebuilt()){t.parent||=this;t.rebuild();t.show()}else this.add(t)}addUndoableEditor(t){this.addCommands({cmd:()=>t._uiManager.rebuild(t),undo:()=>{t.remove()},mustExec:!1})}getNextId(){return this.#m.getId()}get#Xr(){return AnnotationEditorLayer.#z.get(this.#m.getMode())}combinedSignal(t){return this.#m.combinedSignal(t)}#Kr(t){const e=this.#Xr;return e?new e.prototype.constructor(t):null}canCreateNewEmptyEditor(){return this.#Xr?.canCreateNewEmptyEditor()}pasteEditor(t,e){this.#m.updateToolbar(t);this.#m.updateMode(t);const{offsetX:i,offsetY:s}=this.#Yr(),n=this.getNextId(),a=this.#Kr({parent:this,id:n,x:i,y:s,uiManager:this.#m,isCentered:!0,...e});a&&this.add(a)}async deserialize(t){return await(AnnotationEditorLayer.#z.get(t.annotationType??t.annotationEditorType)?.deserialize(t,this,this.#m))||null}createAndAddNewEditor(t,e,i={}){const s=this.getNextId(),n=this.#Kr({parent:this,id:s,x:t.offsetX,y:t.offsetY,uiManager:this.#m,isCentered:e,...i});n&&this.add(n);return n}#Yr(){const{x:t,y:e,width:i,height:s}=this.div.getBoundingClientRect(),n=Math.max(0,t),a=Math.max(0,e),r=(n+Math.min(window.innerWidth,t+i))/2-t,o=(a+Math.min(window.innerHeight,e+s))/2-e,[l,h]=this.viewport.rotation%180==0?[r,o]:[o,r];return{offsetX:l,offsetY:h}}addNewEditor(){this.createAndAddNewEditor(this.#Yr(),!0)}setSelected(t){this.#m.setSelected(t)}toggleSelected(t){this.#m.toggleSelected(t)}isSelected(t){return this.#m.isSelected(t)}unselect(t){this.#m.unselect(t)}pointerup(t){const{isMac:e}=util_FeatureTest.platform;if(!(0!==t.button||t.ctrlKey&&e)&&t.target===this.div&&this.#Ur){this.#Ur=!1;this.#Nr?this.#m.getMode()!==g.STAMP?this.createAndAddNewEditor(t,!1):this.#m.unselectAll():this.#Nr=!0}}pointerdown(t){this.#m.getMode()===g.HIGHLIGHT&&this.enableTextSelection();if(this.#Ur){this.#Ur=!1;return}const{isMac:e}=util_FeatureTest.platform;if(0!==t.button||t.ctrlKey&&e)return;if(t.target!==this.div)return;this.#Ur=!0;const i=this.#m.getActive();this.#Nr=!i||i.isEmpty()}findNewParent(t,e,i){const s=this.#m.findParent(e,i);if(null===s||s===this)return!1;s.changeParent(t);return!0}destroy(){if(this.#m.getActive()?.parent===this){this.#m.commitOrRemove();this.#m.setActiveEditor(null)}if(this.#Hr){clearTimeout(this.#Hr);this.#Hr=null}for(const t of this.#zr.values()){this.#Qs?.removePointerInTextLayer(t.contentDiv);t.setParent(null);t.isAttachedToDOM=!1;t.div.remove()}this.div=null;this.#zr.clear();this.#m.removeLayer(this)}#Vr(){this.#jr=!0;for(const t of this.#zr.values())t.isEmpty()&&t.remove();this.#jr=!1}render({viewport:t}){this.viewport=t;setLayerDimensions(this.div,t);for(const t of this.#m.getEditors(this.pageIndex)){this.add(t);t.rebuild()}this.updateMode()}update({viewport:t}){this.#m.commitOrRemove();this.#Vr();const e=this.viewport.rotation,i=t.rotation;this.viewport=t;setLayerDimensions(this.div,{rotation:i});if(e!==i)for(const t of this.#zr.values())t.rotate(i);this.addInkEditorIfNeeded(!1)}get pageDimensions(){const{pageWidth:t,pageHeight:e}=this.viewport.rawDims;return[t,e]}get scale(){return this.#m.viewParameters.realScale}}class DrawLayer{#Fs=null;#v=0;#Qr=new Map;#Jr=new Map;constructor({pageIndex:t}){this.pageIndex=t}setParent(t){if(this.#Fs){if(this.#Fs!==t){if(this.#Qr.size>0)for(const e of this.#Qr.values()){e.remove();t.append(e)}this.#Fs=t}}else this.#Fs=t}static get _svgFactory(){return shadow(this,"_svgFactory",new DOMSVGFactory)}static#Zr(t,{x:e=0,y:i=0,width:s=1,height:n=1}={}){const{style:a}=t;a.top=100*i+"%";a.left=100*e+"%";a.width=100*s+"%";a.height=100*n+"%"}#to(t){const e=DrawLayer._svgFactory.create(1,1,!0);this.#Fs.append(e);e.setAttribute("aria-hidden",!0);DrawLayer.#Zr(e,t);return e}#eo(t,e){const i=DrawLayer._svgFactory.createElement("clipPath");t.append(i);const s=`clip_${e}`;i.setAttribute("id",s);i.setAttribute("clipPathUnits","objectBoundingBox");const n=DrawLayer._svgFactory.createElement("use");i.append(n);n.setAttribute("href",`#${e}`);n.classList.add("clip");return s}highlight(t,e,i,s=!1){const n=this.#v++,a=this.#to(t.box);a.classList.add("highlight");t.free&&a.classList.add("free");const r=DrawLayer._svgFactory.createElement("defs");a.append(r);const o=DrawLayer._svgFactory.createElement("path");r.append(o);const l=`path_p${this.pageIndex}_${n}`;o.setAttribute("id",l);o.setAttribute("d",t.toSVGPath());s&&this.#Jr.set(n,o);const h=this.#eo(r,l),d=DrawLayer._svgFactory.createElement("use");a.append(d);a.setAttribute("fill",e);a.setAttribute("fill-opacity",i);d.setAttribute("href",`#${l}`);this.#Qr.set(n,a);return{id:n,clipPathId:`url(#${h})`}}highlightOutline(t){const e=this.#v++,i=this.#to(t.box);i.classList.add("highlightOutline");const s=DrawLayer._svgFactory.createElement("defs");i.append(s);const n=DrawLayer._svgFactory.createElement("path");s.append(n);const a=`path_p${this.pageIndex}_${e}`;n.setAttribute("id",a);n.setAttribute("d",t.toSVGPath());n.setAttribute("vector-effect","non-scaling-stroke");let r;if(t.free){i.classList.add("free");const t=DrawLayer._svgFactory.createElement("mask");s.append(t);r=`mask_p${this.pageIndex}_${e}`;t.setAttribute("id",r);t.setAttribute("maskUnits","objectBoundingBox");const n=DrawLayer._svgFactory.createElement("rect");t.append(n);n.setAttribute("width","1");n.setAttribute("height","1");n.setAttribute("fill","white");const o=DrawLayer._svgFactory.createElement("use");t.append(o);o.setAttribute("href",`#${a}`);o.setAttribute("stroke","none");o.setAttribute("fill","black");o.setAttribute("fill-rule","nonzero");o.classList.add("mask")}const o=DrawLayer._svgFactory.createElement("use");i.append(o);o.setAttribute("href",`#${a}`);r&&o.setAttribute("mask",`url(#${r})`);const l=o.cloneNode();i.append(l);o.classList.add("mainOutline");l.classList.add("secondaryOutline");this.#Qr.set(e,i);return e}finalizeLine(t,e){const i=this.#Jr.get(t);this.#Jr.delete(t);this.updateBox(t,e.box);i.setAttribute("d",e.toSVGPath())}updateLine(t,e){this.#Qr.get(t).firstChild.firstChild.setAttribute("d",e.toSVGPath())}removeFreeHighlight(t){this.remove(t);this.#Jr.delete(t)}updatePath(t,e){this.#Jr.get(t).setAttribute("d",e.toSVGPath())}updateBox(t,e){DrawLayer.#Zr(this.#Qr.get(t),e)}show(t,e){this.#Qr.get(t).classList.toggle("hidden",!e)}rotate(t,e){this.#Qr.get(t).setAttribute("data-main-rotation",e)}changeColor(t,e){this.#Qr.get(t).setAttribute("fill",e)}changeOpacity(t,e){this.#Qr.get(t).setAttribute("fill-opacity",e)}addClass(t,e){this.#Qr.get(t).classList.add(e)}removeClass(t,e){this.#Qr.get(t).classList.remove(e)}getSVGRoot(t){return this.#Qr.get(t)}remove(t){if(null!==this.#Fs){this.#Qr.get(t).remove();this.#Qr.delete(t)}}destroy(){this.#Fs=null;for(const t of this.#Qr.values())t.remove();this.#Qr.clear()}}var te=__webpack_exports__.AbortException,ee=__webpack_exports__.AnnotationEditorLayer,ie=__webpack_exports__.AnnotationEditorParamsType,se=__webpack_exports__.AnnotationEditorType,ne=__webpack_exports__.AnnotationEditorUIManager,ae=__webpack_exports__.AnnotationLayer,re=__webpack_exports__.AnnotationMode,oe=__webpack_exports__.CMapCompressionType,le=__webpack_exports__.ColorPicker,he=__webpack_exports__.DOMSVGFactory,de=__webpack_exports__.DrawLayer,ce=__webpack_exports__.FeatureTest,ue=__webpack_exports__.GlobalWorkerOptions,pe=__webpack_exports__.ImageKind,ge=__webpack_exports__.InvalidPDFException,me=__webpack_exports__.MissingPDFException,fe=__webpack_exports__.OPS,be=__webpack_exports__.OutputScale,Ae=__webpack_exports__.PDFDataRangeTransport,ve=__webpack_exports__.PDFDateString,ye=__webpack_exports__.PDFWorker,we=__webpack_exports__.PasswordResponses,xe=__webpack_exports__.PermissionFlag,_e=__webpack_exports__.PixelsPerInch,Ee=__webpack_exports__.RenderingCancelledException,Ce=__webpack_exports__.TextLayer,Se=__webpack_exports__.UnexpectedResponseException,Te=__webpack_exports__.Util,Me=__webpack_exports__.VerbosityLevel,ke=__webpack_exports__.XfaLayer,Pe=__webpack_exports__.build,Fe=__webpack_exports__.createValidAbsoluteUrl,De=__webpack_exports__.fetchData,Re=__webpack_exports__.getDocument,Ie=__webpack_exports__.getFilenameFromUrl,Le=__webpack_exports__.getPdfFilenameFromUrl,Ne=__webpack_exports__.getXfaPageViewport,Oe=__webpack_exports__.isDataScheme,Be=__webpack_exports__.isPdfFile,He=__webpack_exports__.noContextMenu,ze=__webpack_exports__.normalizeUnicode,Ue=__webpack_exports__.setLayerDimensions,je=__webpack_exports__.shadow,We=__webpack_exports__.version;export{te as AbortException,ee as AnnotationEditorLayer,ie as AnnotationEditorParamsType,se as AnnotationEditorType,ne as AnnotationEditorUIManager,ae as AnnotationLayer,re as AnnotationMode,oe as CMapCompressionType,le as ColorPicker,he as DOMSVGFactory,de as DrawLayer,ce as FeatureTest,ue as GlobalWorkerOptions,pe as ImageKind,ge as InvalidPDFException,me as MissingPDFException,fe as OPS,be as OutputScale,Ae as PDFDataRangeTransport,ve as PDFDateString,ye as PDFWorker,we as PasswordResponses,xe as PermissionFlag,_e as PixelsPerInch,Ee as RenderingCancelledException,Ce as TextLayer,Se as UnexpectedResponseException,Te as Util,Me as VerbosityLevel,ke as XfaLayer,Pe as build,Fe as createValidAbsoluteUrl,De as fetchData,Re as getDocument,Ie as getFilenameFromUrl,Le as getPdfFilenameFromUrl,Ne as getXfaPageViewport,Oe as isDataScheme,Be as isPdfFile,He as noContextMenu,ze as normalizeUnicode,Ue as setLayerDimensions,je as shadow,We as version}; \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/Bit.BlazorUI.Icons.csproj b/src/BlazorUI/Bit.BlazorUI.Icons/Bit.BlazorUI.Icons.csproj index 32b08f24d7..358bb5f7dd 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/Bit.BlazorUI.Icons.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Icons/Bit.BlazorUI.Icons.csproj @@ -36,7 +36,7 @@ </Target> <Target Name="BuildCss" Inputs="@(ScssFiles)" Outputs="wwwroot/styles/bit.blazorui.icons.css"> - <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.icons.scss:wwwroot/styles/bit.blazorui.icons.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.icons.scss:wwwroot/styles/bit.blazorui.icons.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/Styles/fabric.mdl2.icons.scss b/src/BlazorUI/Bit.BlazorUI.Icons/Styles/fabric.mdl2.icons.scss index 61063613fd..c15b495fa4 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/Styles/fabric.mdl2.icons.scss +++ b/src/BlazorUI/Bit.BlazorUI.Icons/Styles/fabric.mdl2.icons.scss @@ -1,16 +1,16 @@ @font-face { - font-family: 'Fabric MDL2'; - src: url('../fonts/FabMDL2.4.66.woff2') format("woff2"); - font-weight: normal; font-style: normal; font-display: swap; + font-weight: normal; + font-family: 'Fabric MDL2'; + src: url('../fonts/FabMDL2.4.66.woff2') format("woff2"); } .bit-icon { - display: inline-block; - font-family: 'Fabric MDL2' !important; font-style: normal; font-weight: normal; + display: inline-block; + font-family: 'Fabric MDL2' !important; } .bit-icon--12PointStar:before { content: "\F505"; } diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json index 41826ee258..7acb7a7739 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json @@ -5,32 +5,293 @@ "packages": { "": { "devDependencies": { - "sass": "1.77.8" + "sass": "1.80.5" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -38,6 +299,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -46,27 +308,32 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/fill-range": { @@ -74,6 +341,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -81,55 +349,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -139,6 +370,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -151,24 +383,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -177,24 +423,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -219,6 +469,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package.json b/src/BlazorUI/Bit.BlazorUI.Icons/package.json index ea5cb1f7da..86b9c82cc5 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.77.8" + "sass": "1.80.5" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj index 49f9ab1df6..70cf41fb96 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj @@ -13,10 +13,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="bunit.web" Version="1.31.3" /> + <PackageReference Include="bunit.web" Version="1.34.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> - <PackageReference Include="MSTest.TestAdapter" Version="3.6.0" /> - <PackageReference Include="MSTest.TestFramework" Version="3.6.0" /> + <PackageReference Include="MSTest.TestAdapter" Version="3.6.2" /> + <PackageReference Include="MSTest.TestFramework" Version="3.6.2" /> <PackageReference Include="coverlet.collector" Version="6.0.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectTestModel.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectTestModel.cs index 15ebfe078d..acb7324617 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectTestModel.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectTestModel.cs @@ -8,5 +8,5 @@ public class BitDropdownMultiSelectTestModel [Required] [MaxLength(2)] [MinLength(2)] - public ICollection<string> Values { get; set; } + public IEnumerable<string> Values { get; set; } } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectValidationTest.razor b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectValidationTest.razor index 16f4778dd4..0e7b97c446 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectValidationTest.razor +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownMultiSelectValidationTest.razor @@ -4,7 +4,7 @@ <BitDropdown @bind-Values="TestModel.Values" IsEnabled="IsEnabled" OnClick="HandleClick" - IsMultiSelect="IsMultiSelect" + MultiSelect="IsMultiSelect" IsOpen="IsOpen" Items="Items" MultiSelectDelimiter="@MultiSelectDelimiter" @@ -13,7 +13,7 @@ Placeholder="@Placeholder" Label="@Label" Title="@Title" - IsReselectable="IsReselectable" + Reselectable="IsReselectable" OnSelectItem="HandleSelectItem" Required="Required" TItem="BitDropdownItem<string>" diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs index c2d9e49ee7..84b5f9207c 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs @@ -11,7 +11,7 @@ namespace Bit.BlazorUI.Tests.Components.Inputs.Dropdown; public class BitDropdownTests : BunitTestContext { private string _bitDropdownValue; - private ICollection<string> _bitDropdownValues; + private IEnumerable<string> _bitDropdownValues; [DataTestMethod, DataRow(true), @@ -78,7 +78,7 @@ public void ResponsiveDropdownShouldRenderLabel(string labelFragment) var component = RenderComponent<BitDropdown<BitDropdownItem<string>, string>>(parameters => { - parameters.Add(p => p.IsResponsive, true); + parameters.Add(p => p.Responsive, true); parameters.Add(p => p.Label, labelFragment); }); @@ -102,7 +102,7 @@ public void ResponsiveDropdownShouldRenderLabelFragment(string labelFragment) var component = RenderComponent<BitDropdown<BitDropdownItem<string>, string>>(parameters => { - parameters.Add(p => p.IsResponsive, true); + parameters.Add(p => p.Responsive, true); if (string.IsNullOrEmpty(labelFragment) is false) { @@ -155,7 +155,7 @@ public void BitDropdownIsMultiSelectShouldWorkCorrect(bool isMultiSelect) { parameters.Add(p => p.IsOpen, isOpen); parameters.Add(p => p.Items, items); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); }); var bitDropdown = component.Find(".bit-drp"); @@ -184,7 +184,7 @@ public void BitDropdownItemsShouldRenderCorrect(bool isMultiSelect) parameters.Add(p => p.IsOpen, isOpen); parameters.Add(p => p.IsOpenChanged, v => isOpen = v); parameters.Add(p => p.Items, items); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); }); Assert.AreEqual(items.FindAll(i => i.ItemType == BitDropdownItemType.Header).Count, component.FindAll(".bit-drp-ihd").Count); @@ -234,7 +234,7 @@ public void BitDropdownTextWithDefaultValuesShouldInitCorrect(string defaultValu var component = RenderComponent<BitDropdown<BitDropdownItem<string>, string>>(parameters => { parameters.Add(p => p.Items, items); - parameters.Add(p => p.IsMultiSelect, true); + parameters.Add(p => p.MultiSelect, true); parameters.Add(p => p.DefaultValues, defaultSelectedMultipleValueList); }); @@ -289,7 +289,7 @@ public void BitDropdownTextWithValuesAndDefaultValuesShouldInitCorrect(string de var component = RenderComponent<BitDropdown<BitDropdownItem<string>, string>>(parameters => { parameters.Add(p => p.Items, items); - parameters.Add(p => p.IsMultiSelect, true); + parameters.Add(p => p.MultiSelect, true); parameters.Add(p => p.DefaultValues, defaultSelectedMultipleValueList); parameters.Add(p => p.Values, selectedMultipleValueList); }); @@ -326,7 +326,7 @@ public void BitDropdownPlaceholderShouldWorkCorrect(string value, string values, var component = RenderComponent<BitDropdown<BitDropdownItem<string>, string>>(parameters => { parameters.Add(p => p.Items, items); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); parameters.Add(p => p.Values, selectedMultipleValueList); parameters.Add(p => p.Value, value); parameters.Add(p => p.Placeholder, placeholder); @@ -444,7 +444,7 @@ public void BitDropdownNotifyOnReselectShouldWorkCorrect(bool notifyOnReselect, parameters.Add(p => p.IsOpen, isOpen); parameters.Add(p => p.IsOpenChanged, v => isOpen = v); parameters.Add(p => p.IsEnabled, true); - parameters.Add(p => p.IsReselectable, notifyOnReselect); + parameters.Add(p => p.Reselectable, notifyOnReselect); parameters.Add(p => p.DefaultValue, defaultValue); parameters.Add(p => p.OnSelectItem, () => itemSelected = true); }); @@ -479,7 +479,7 @@ public void BitDropdownEnableItemSelectionShouldWorkCorrect(bool itemIsEnabled, parameters.Add(p => p.IsOpen, isOpen); parameters.Add(p => p.IsOpenChanged, v => isOpen = v); parameters.Add(p => p.IsEnabled, true); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); parameters.Add(p => p.OnValuesChange, () => itemsSelected++); }); @@ -547,7 +547,7 @@ public void BitDropdownMultiSelectTwoWayBoundWithCustomHandlerShouldWorkCorrect( parameters.Add(p => p.IsOpen, isOpen); parameters.Add(p => p.IsOpenChanged, v => isOpen = v); parameters.Add(p => p.IsEnabled, true); - parameters.Add(p => p.IsMultiSelect, true); + parameters.Add(p => p.MultiSelect, true); parameters.Add(p => p.Items, items); parameters.Add(p => p.Values, _bitDropdownValues); parameters.Add(p => p.ValuesChanged, HandleValuesChanged); @@ -725,7 +725,7 @@ public void BitDropdownMultiSelectValidationInvalidHtmlAttributeTest(string valu parameters.Add(p => p.TestModel, new BitDropdownMultiSelectTestModel { Values = _bitDropdownValues }); }); - var isInvalid = (_bitDropdownValues?.Count ?? 0) != 2; + var isInvalid = (_bitDropdownValues?.Count() ?? 0) != 2; var selectTag = component.Find("select"); Assert.IsFalse(selectTag.HasAttribute("aria-invalid")); @@ -853,7 +853,7 @@ public void BitDropdownSearchItemTest(string search, bool isMultiSelect) { parameters.Add(p => p.IsEnabled, true); parameters.Add(p => p.ShowSearchBox, true); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); parameters.Add(p => p.Items, items); }); @@ -907,7 +907,7 @@ public void BitDropdownVirtualizeTest(bool virtualize, int? itemSize, int? overs { parameters.Add(p => p.IsEnabled, true); parameters.Add(p => p.Virtualize, virtualize); - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); parameters.Add(p => p.Items, items); if (itemSize.HasValue) @@ -969,7 +969,8 @@ public void BitDropdownCaretDownIconNameTest(string? iconName) } else { - Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-icon--ChevronDown")); + Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-ico-r90")); + Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-icon--ChevronRight")); } } @@ -989,7 +990,8 @@ public void BitDropdownCaretDownTemplateTest(string iconFragment) if (string.IsNullOrEmpty(iconFragment)) { - Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-icon--ChevronDown")); + Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-ico-r90")); + Assert.IsTrue(component.Find(".bit-drp-wrp > .bit-drp-icn > i").ClassList.Contains("bit-icon--ChevronRight")); } else { @@ -1026,7 +1028,7 @@ private void HandleValueChanged(string value) _bitDropdownValue = value; } - private void HandleValuesChanged(ICollection<string> values) + private void HandleValuesChanged(IEnumerable<string> values) { _bitDropdownValues = values; } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownValidationTest.razor b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownValidationTest.razor index 47f81de98c..3681997e2b 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownValidationTest.razor +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownValidationTest.razor @@ -4,7 +4,7 @@ <BitDropdown @bind-IsOpen="IsOpen" @bind-Value="@TestModel.Value" IsEnabled="IsEnabled" OnClick="HandleClick" - IsMultiSelect="IsMultiSelect" + MultiSelect="IsMultiSelect" Items="Items" MultiSelectDelimiter="@MultiSelectDelimiter" DefaultValues="DefaultValues" @@ -12,7 +12,7 @@ Placeholder="@Placeholder" Label="@Label" Title="@Title" - IsReselectable="IsReselectable" + Reselectable="IsReselectable" OnSelectItem="HandleSelectItem" Required="Required" TItem="BitDropdownItem<string>" diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/FileUpload/BitFileUploadTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/FileUpload/BitFileUploadTests.cs index 5c16f55e96..2d724f6ef3 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/FileUpload/BitFileUploadTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/FileUpload/BitFileUploadTests.cs @@ -31,7 +31,7 @@ public void BitFileUploadMultipleAttributeTest(bool isMultiSelect) { var com = RenderComponent<BitFileUpload>(parameters => { - parameters.Add(p => p.IsMultiSelect, isMultiSelect); + parameters.Add(p => p.MultiSelect, isMultiSelect); }); var bitFileUpload = com.Find(".bit-upl-fi"); diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs index 0242a80ff8..ee9a2a5560 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs @@ -134,7 +134,7 @@ public void BitSearchBoxAutoCompleteTest(string autoComplete) { var component = RenderComponent<BitSearchBox>(parameters => { - parameters.Add(p => p.Autocomplete, autoComplete); + parameters.Add(p => p.AutoComplete, autoComplete); parameters.Add(p => p.IsEnabled, true); }); diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldTests.cs index 1766d889d0..291839d522 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldTests.cs @@ -13,12 +13,12 @@ public class BitTextFieldTests : BunitTestContext DataRow(true, false, true), DataRow(false, false, false) ] - public void BitTextFieldShouldTakeCorrectTypeAndVisual(bool isEnabled, bool isMultiline, bool required) + public void BitTextFieldShouldTakeCorrectTypeAndVisual(bool isEnabled, bool multiline, bool required) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.IsEnabled, isEnabled); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.Required, required); }); @@ -34,7 +34,7 @@ public void BitTextFieldShouldTakeCorrectTypeAndVisual(bool isEnabled, bool isMu Assert.IsTrue(bitTextField.ClassList.Contains("bit-dis")); } - Assert.AreEqual(isMultiline ? "TEXTAREA" : "INPUT", textField.TagName); + Assert.AreEqual(multiline ? "TEXTAREA" : "INPUT", textField.TagName); Assert.AreEqual(required, textField.HasAttribute("required")); Assert.AreEqual(required, bitTextField.ClassList.Contains("bit-tfl-req")); @@ -44,12 +44,12 @@ public void BitTextFieldShouldTakeCorrectTypeAndVisual(bool isEnabled, bool isMu DataRow(true, "hello world"), DataRow(false, "hello world") ] - public void BitTextFieldShouldTakeValue(bool isMultiline, string value) + public void BitTextFieldShouldTakeValue(bool multiline, string value) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.Value, value); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); }); var bitTextField = component.Find(".bit-tfl-inp"); @@ -76,12 +76,12 @@ public void BitTextFieldLabel(string label) DataRow(15, false, "this is placeholder", true), DataRow(15, false, "this is placeholder", false), ] - public void BitTextFieldShouldTakeBaseParameters(int maxLength, bool isMultiline, string placeholder, bool readOnly) + public void BitTextFieldShouldTakeBaseParameters(int maxLength, bool multiline, string placeholder, bool readOnly) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.MaxLength, maxLength); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.Placeholder, placeholder); parameters.Add(p => p.ReadOnly, readOnly); }); @@ -159,13 +159,13 @@ public void BitInputTypeTest(BitInputType type) DataRow(false, false), DataRow(false, true), ] - public void BitTextFieldMustRespondToTheClickEvent(bool isEnabled, bool isMultiline) + public void BitTextFieldMustRespondToTheClickEvent(bool isEnabled, bool multiline) { int currentCount = 0; var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.IsEnabled, isEnabled); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.OnClick, () => currentCount++); }); @@ -182,7 +182,7 @@ public void BitTextFieldMustRespondToTheClickEvent(bool isEnabled, bool isMultil DataRow(false, false), DataRow(false, true), ] - public void BitTextFieldMustRespondToTheFocusEvent(bool isEnabled, bool isMultiline) + public void BitTextFieldMustRespondToTheFocusEvent(bool isEnabled, bool multiline) { int focusedValue = 0; int focusedInValue = 0; @@ -190,7 +190,7 @@ public void BitTextFieldMustRespondToTheFocusEvent(bool isEnabled, bool isMultil var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.IsEnabled, isEnabled); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.OnFocus, () => focusedValue++); parameters.Add(p => p.OnFocusIn, () => focusedInValue++); parameters.Add(p => p.OnFocusOut, () => focusedOutValue++); @@ -214,14 +214,14 @@ public void BitTextFieldMustRespondToTheFocusEvent(bool isEnabled, bool isMultil DataRow(false, false, "u", "d"), DataRow(false, true, "u", "d"), ] - public void BitTextFieldMustRespondToTheKeyEvent(bool isEnabled, bool isMultiline, string keyUpValue, string keyDownValue) + public void BitTextFieldMustRespondToTheKeyEvent(bool isEnabled, bool multiline, string keyUpValue, string keyDownValue) { string? keyUppedValue = null; string? keyDownedValue = null; var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.IsEnabled, isEnabled); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.OnKeyUp, (KeyboardEventArgs e) => keyUppedValue = e.Key); parameters.Add(p => p.OnKeyDown, (KeyboardEventArgs e) => keyDownedValue = e.Key); }); @@ -241,13 +241,13 @@ public void BitTextFieldMustRespondToTheKeyEvent(bool isEnabled, bool isMultilin DataRow(false, false), DataRow(false, true), ] - public void BitTextFieldMustRespondToTheChangeEvent(bool isEnabled, bool isMultiline) + public void BitTextFieldMustRespondToTheChangeEvent(bool isEnabled, bool multiline) { int currentCount = 0; var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.IsEnabled, isEnabled); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); parameters.Add(p => p.OnChange, () => currentCount++); }); @@ -286,12 +286,12 @@ public void BitTextFieldShowSuffix(string suffix) DataRow(true, "hello world"), DataRow(false, "hello world"), ] - public void BitTextFieldShouldTakeDefaultValue(bool isMultiline, string defaultValue) + public void BitTextFieldShouldTakeDefaultValue(bool multiline, string defaultValue) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.DefaultValue, defaultValue); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); }); var bitTextField = component.Find(".bit-tfl-inp"); @@ -315,30 +315,30 @@ public void BitTextFieldShouldTakeDescription(string description) DataRow(true), DataRow(false) ] - public void BitTextFieldShouldRespectUnderLineStyle(bool isUnderlined) + public void BitTextFieldShouldRespectUnderLineStyle(bool underlined) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsUnderlined, isUnderlined); + parameters.Add(p => p.Underlined, underlined); }); var bitTextField = component.Find(".bit-tfl"); - Assert.AreEqual(isUnderlined, bitTextField.ClassList.Contains("bit-tfl-und")); + Assert.AreEqual(underlined, bitTextField.ClassList.Contains("bit-tfl-und")); } [DataTestMethod, DataRow(true), DataRow(false) ] - public void BitTextFieldShouldRespectHasBorderStyle(bool hasBorder) + public void BitTextFieldShouldRespectHasBorderStyle(bool noBorder) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.HasBorder, hasBorder); + parameters.Add(p => p.NoBorder, noBorder); }); var bitTextField = component.Find(".bit-tfl"); - Assert.AreEqual(hasBorder is false, bitTextField.ClassList.Contains("bit-tfl-nbd")); + Assert.AreEqual(noBorder, bitTextField.ClassList.Contains("bit-tfl-nbd")); } [DataTestMethod, DataRow(5)] @@ -346,7 +346,7 @@ public void BitTextFieldShouldRespectRowsNumberWhenItIsMultiline(int rows) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsMultiline, true); + parameters.Add(p => p.Multiline, true); parameters.Add(p => p.Rows, rows); }); @@ -358,28 +358,28 @@ public void BitTextFieldShouldRespectRowsNumberWhenItIsMultiline(int rows) DataRow(true), DataRow(false) ] - public void BitTextFieldShouldRespectIsResizableWhenItIsMultiline(bool isResizable) + public void BitTextFieldShouldRespectIsResizableWhenItIsMultiline(bool resizable) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsMultiline, true); - parameters.Add(p => p.IsResizable, isResizable); + parameters.Add(p => p.Multiline, true); + parameters.Add(p => p.Resizable, resizable); }); var bitTextField = component.Find(".bit-tfl"); - Assert.AreEqual(!isResizable, bitTextField.ClassList.Contains("bit-tfl-mlf")); + Assert.AreEqual(!resizable, bitTextField.ClassList.Contains("bit-tfl-mlf")); } [DataTestMethod, DataRow("Detailed label", true), DataRow("Detailed label", false) ] - public void BitTextFieldAriaLabelTest(string ariaLabel, bool isMultiline) + public void BitTextFieldAriaLabelTest(string ariaLabel, bool multiline) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.AriaLabel, ariaLabel); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); }); @@ -395,12 +395,12 @@ public void BitTextFieldAriaLabelTest(string ariaLabel, bool isMultiline) DataRow(true, null), DataRow(false, null) ] - public void BitTextFieldAriaLabelledbyTest(bool isMultiline, string label) + public void BitTextFieldAriaLabelledbyTest(bool multiline, string label) { var component = RenderComponent<BitTextField>(parameters => { parameters.Add(p => p.Label, label); - parameters.Add(p => p.IsMultiline, isMultiline); + parameters.Add(p => p.Multiline, multiline); }); var input = component.Find(".bit-tfl-inp"); @@ -416,7 +416,7 @@ public void BitTextFieldAutoCompleteTest(string autoComplete) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsMultiline, false); + parameters.Add(p => p.Multiline, false); parameters.Add(p => p.AutoComplete, autoComplete); }); @@ -484,7 +484,7 @@ public void BitTextFieldValidationFormTestWhenItIsMultiline(string value) { parameters.Add(p => p.TestModel, new BitTextFieldTestModel { Value = value }); parameters.Add(p => p.IsEnabled, true); - parameters.Add(p => p.IsMultiline, true); + parameters.Add(p => p.Multiline, true); }); var isValid = value == "test@bit.com"; @@ -564,7 +564,7 @@ public void BitTextFieldValidationInvalidHtmlAttributeTestWhenItIsMultiline(stri { parameters.Add(p => p.TestModel, new BitTextFieldTestModel { Value = value }); parameters.Add(p => p.IsEnabled, true); - parameters.Add(p => p.IsMultiline, true); + parameters.Add(p => p.Multiline, true); }); var isInvalid = value != "test@bit.com"; @@ -640,7 +640,7 @@ public void BitTextFieldTrimmedDefaultValueTest(string value) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsTrimmed, true); + parameters.Add(p => p.Trim, true); parameters.Add(p => p.DefaultValue, value); }); @@ -660,7 +660,7 @@ public void BitTextFieldTrimmedValueTest(string value) { var component = RenderComponent<BitTextField>(parameters => { - parameters.Add(p => p.IsTrimmed, true); + parameters.Add(p => p.Trim, true); }); var input = component.Find("input"); diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldValidationTest.razor b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldValidationTest.razor index a7d6c1b091..ddf5786259 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldValidationTest.razor +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/TextField/BitTextFieldValidationTest.razor @@ -2,7 +2,7 @@ <DataAnnotationsValidator /> <BitTextField MaxLength="MaxLength" - IsMultiline="IsMultiline" + Multiline="Multiline" Type="Type" Placeholder="@Placeholder" ReadOnly="ReadOnly" @@ -21,14 +21,14 @@ OnKeyUp="HandleKeyUp" AriaLabel="@AriaLabel" Label="@Label" - IsUnderlined="IsUnderlined" - HasBorder="HasBorder" + Underlined="Underlined" + NoBorder="NoBorder" DefaultValue="@DefaultValue" Description="@Description" Prefix="@Prefix" Suffix="@Suffix" Rows="Rows" - IsResizable="IsResizable" + Resizable="Resizable" AutoComplete="@AutoComplete"> </BitTextField> @@ -37,7 +37,7 @@ @code { [Parameter] public int MaxLength { get; set; } = -1; [Parameter] public string IconName { get; set; } - [Parameter] public bool IsMultiline { get; set; } + [Parameter] public bool Multiline { get; set; } [Parameter] public bool IsEnabled { get; set; } = true; [Parameter] public bool ReadOnly { get; set; } [Parameter] public bool Required { get; set; } @@ -52,9 +52,9 @@ [Parameter] public string DefaultValue { get; set; } [Parameter] public string Description { get; set; } [Parameter] public string ErrorMessage { get; set; } - [Parameter] public bool IsUnderlined { get; set; } - [Parameter] public bool HasBorder { get; set; } - [Parameter] public bool IsResizable { get; set; } + [Parameter] public bool Underlined { get; set; } + [Parameter] public bool NoBorder { get; set; } + [Parameter] public bool Resizable { get; set; } [Parameter] public int Rows { get; set; } [Parameter] public string AutoComplete { get; set; } [Parameter] public BitTextFieldTestModel TestModel { get; set; } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleTests.cs index 8782b466c9..44b6c83447 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleTests.cs @@ -49,7 +49,7 @@ public void BitToggleInlineLabelShouldHaveClassName(bool isInlineLabel) { var com = RenderComponent<BitToggle>(parameters => { - parameters.Add(p => p.IsInlineLabel, isInlineLabel); + parameters.Add(p => p.Inline, isInlineLabel); }); var bitToggle = com.Find(".bit-tgl"); @@ -89,7 +89,7 @@ public void BitToggleAriaLabelledbyTest(bool value, string onText, string offTex parameters.Add(p => p.Value, value); parameters.Add(p => p.OnText, onText); parameters.Add(p => p.OffText, offText); - parameters.Add(p => p.DefaultText, defaultText); + parameters.Add(p => p.Text, defaultText); parameters.Add(p => p.AriaLabel, string.Empty); parameters.Add(p => p.Label, label); }); diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleValidationTest.razor b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleValidationTest.razor index 071026fd6b..83cdc731fc 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleValidationTest.razor +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Toggle/BitToggleValidationTest.razor @@ -3,7 +3,7 @@ <BitToggle IsEnabled="IsEnabled" @bind-Value="@TestModel.Value" - IsInlineLabel="IsInlineLabel" + Inline="IsInlineLabel" OnText="@OnText" OffText="@OffText" AriaLabel="@AriaLabel" diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbTests.cs index 029d6c0b55..44056249b8 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbTests.cs @@ -26,7 +26,7 @@ public void BitBreadcrumbShouldTakeDividerIcon(string icon) [DataTestMethod, DataRow((uint)0), DataRow((uint)3) - ] + ] public void BitBreadcrumbShouldRespectMaxDisplayItems(uint maxDisplayedItems) { var breadcrumbItems = GetBreadcrumbItems(); @@ -37,11 +37,11 @@ public void BitBreadcrumbShouldRespectMaxDisplayItems(uint maxDisplayedItems) parameters.Add(p => p.MaxDisplayedItems, maxDisplayedItems); }); - var breadcrumbElements = component.FindAll(".bit-brc .bit-brc-iwp ul li"); + var breadcrumbElements = component.FindAll(".bit-brc ul.bit-brc-icn li a"); if (maxDisplayedItems > 0) { - Assert.AreEqual((uint)breadcrumbElements.Count, maxDisplayedItems + 1); + Assert.AreEqual((uint)breadcrumbElements.Count, maxDisplayedItems); } else { @@ -63,15 +63,14 @@ public void BitBreadcrumbShouldRespectOverflowChanges(string icon, uint maxDispl parameters.Add(p => p.MaxDisplayedItems, maxDisplayedItems); }); - var breadcrumbOverflowIcon = component.Find(".bit-brc ul li button span i"); + var breadcrumbOverflowIcon = component.Find(".bit-brc ul.bit-brc-icn li button i"); Assert.IsTrue(breadcrumbOverflowIcon.ClassList.Contains($"bit-icon--{icon}")); - var breadcrumbElements = component.FindAll(".bit-brc .bit-brc-iwp ul li"); + var breadcrumbElements = component.FindAll(".bit-brc ul.bit-brc-icn li a"); var overflowItem = breadcrumbElements[(int)overflowIndex]; - Assert.AreEqual((uint)breadcrumbElements.Count, maxDisplayedItems + 1); - Assert.IsTrue(overflowItem.InnerHtml.Contains("button")); + Assert.AreEqual((uint)breadcrumbElements.Count, maxDisplayedItems); } [DataTestMethod] diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavTests.cs index 446f150195..69de825e31 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavTests.cs @@ -62,15 +62,15 @@ public void BitNavLinkItemAriaLabelTest(string collapseAriaLabel, string expandA { List<BitNavItem> navLinkItems = new() { - new BitNavItem() + new() { Text = "Home", CollapseAriaLabel = collapseAriaLabel, ExpandAriaLabel = expandAriaLabel, IsExpanded = isExpanded, - ChildItems = new List<BitNavItem>() + ChildItems = new() { - new BitNavItem() { Text = "Activity", Url = "http://msn.com", Title = "Activity" } + new() { Text = "Activity", Url = "http://msn.com", Title = "Activity" } } } }; @@ -81,7 +81,7 @@ public void BitNavLinkItemAriaLabelTest(string collapseAriaLabel, string expandA parameters.Add(p => p.Mode, BitNavMode.Manual); }); - var button = component.Find("button"); + var button = component.Find(".bit-nav-cbt"); var expectedResult = isExpanded ? collapseAriaLabel : expandAriaLabel; Assert.AreEqual(expectedResult, button.GetAttribute("aria-label")); @@ -196,12 +196,12 @@ public void BitNavLinkItemTitleTest(string title, string name) new() { Text = name, Title = title, IsExpanded = true } }; - var componenet = RenderComponent<BitNavTest>(parameters => + var component = RenderComponent<BitNavTest>(parameters => { parameters.Add(p => p.Items, items); }); - var navLinkItem = componenet.Find(".bit-nav-itm"); + var navLinkItem = component.Find(".bit-nav-ict"); if (title is null) { diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Pivot/BitPivotTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Pivot/BitPivotTests.cs index 5aabe78aa0..d0d6c46398 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Pivot/BitPivotTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Navs/Pivot/BitPivotTests.cs @@ -7,27 +7,27 @@ namespace Bit.BlazorUI.Tests.Components.Navs.Pivot; public class BitPivotTests : BunitTestContext { [DataTestMethod, - DataRow(BitPivotLinkFormat.Links, BitSize.Large, BitPivotOverflowBehavior.None), - DataRow(BitPivotLinkFormat.Tabs, BitSize.Medium, BitPivotOverflowBehavior.Scroll), - DataRow(BitPivotLinkFormat.Tabs, BitSize.Small, BitPivotOverflowBehavior.Menu) + DataRow(BitPivotHeaderType.Link, BitSize.Large, BitPivotOverflowBehavior.None), + DataRow(BitPivotHeaderType.Tab, BitSize.Medium, BitPivotOverflowBehavior.Scroll), + DataRow(BitPivotHeaderType.Tab, BitSize.Small, BitPivotOverflowBehavior.Menu) ] - public void BitPivotShouldRespectLinkFormatClasses(BitPivotLinkFormat linkFormat, BitSize linkSize, BitPivotOverflowBehavior overflowBehavior) + public void BitPivotShouldRespectParameterRelatedCssClasses(BitPivotHeaderType headerType, BitSize size, BitPivotOverflowBehavior overflowBehavior) { var component = RenderComponent<BitPivot>(parameters => { - parameters.Add(p => p.LinkFormat, linkFormat); - parameters.Add(p => p.LinkSize, linkSize); + parameters.Add(p => p.Size, size); + parameters.Add(p => p.HeaderType, headerType); parameters.Add(p => p.OverflowBehavior, overflowBehavior); }); - var linkSizeClass = $"bit-pvt-{linkSize switch { BitSize.Small => "sm", BitSize.Medium => "md", BitSize.Large => "lg", _ => "md" }}"; - var linkFormatClass = $"bit-pvt-{linkFormat.ToString().ToLower()}"; - var overflowBehaviorClass = $"bit-pvt-{overflowBehavior.ToString().ToLower()}"; + var sizeClass = $"bit-pvt-{size switch { BitSize.Small => "sm", BitSize.Medium => "md", BitSize.Large => "lg", _ => "md" }}"; + var headerTypeClass = $"bit-pvt-{headerType switch { BitPivotHeaderType.Link => "lnk", BitPivotHeaderType.Tab => "tab", _ => "lnk" }}"; + var overflowBehaviorClass = $"bit-pvt-{overflowBehavior switch { BitPivotOverflowBehavior.Menu => "mnu", BitPivotOverflowBehavior.Scroll => "scr", BitPivotOverflowBehavior.None => "non", _ => "non" }}"; var bitPivot = component.Find(".bit-pvt"); - Assert.IsTrue(bitPivot.ClassList.Contains(linkFormatClass)); - Assert.IsTrue(bitPivot.ClassList.Contains(linkSizeClass)); + Assert.IsTrue(bitPivot.ClassList.Contains(sizeClass)); + Assert.IsTrue(bitPivot.ClassList.Contains(headerTypeClass)); Assert.IsTrue(bitPivot.ClassList.Contains(overflowBehaviorClass)); } @@ -84,9 +84,9 @@ public void BitPivotShouldRespectPosition(BitPivotPosition position) var positionClass = position switch { BitPivotPosition.Top => "bit-pvt-top", - BitPivotPosition.Bottom => "bit-pvt-bottom", - BitPivotPosition.Left => "bit-pvt-left", - BitPivotPosition.Right => "bit-pvt-right", + BitPivotPosition.Bottom => "bit-pvt-btm", + BitPivotPosition.Left => "bit-pvt-lft", + BitPivotPosition.Right => "bit-pvt-rgt", _ => string.Empty }; diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/Message/BitMessageTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/Message/BitMessageTests.cs index e6e005b762..40b516531c 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/Message/BitMessageTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/Message/BitMessageTests.cs @@ -88,7 +88,7 @@ public void BitMessageShouldRespectCustomDismissIcon(string iconName) { var component = RenderComponent<BitMessage>(parameters => { - parameters.Add(p => p.DismissIconName, iconName); + parameters.Add(p => p.DismissIcon, iconName); parameters.Add(p => p.OnDismiss, () => { }); }); diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/SnackBar/BitSnackBarTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/SnackBar/BitSnackBarTests.cs index 5d8c866ce5..dcf1c50b01 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/SnackBar/BitSnackBarTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Notifications/SnackBar/BitSnackBarTests.cs @@ -11,12 +11,12 @@ namespace Bit.BlazorUI.Tests.Components.Notifications.SnackBar; public class BitSnackBarTests : BunitTestContext { [DataTestMethod, - DataRow(BitSnackBarPosition.TopLeft), + DataRow(BitSnackBarPosition.TopStart), DataRow(BitSnackBarPosition.TopCenter), - DataRow(BitSnackBarPosition.TopRight), - DataRow(BitSnackBarPosition.BottomLeft), + DataRow(BitSnackBarPosition.TopEnd), + DataRow(BitSnackBarPosition.BottomStart), DataRow(BitSnackBarPosition.BottomCenter), - DataRow(BitSnackBarPosition.BottomRight), + DataRow(BitSnackBarPosition.BottomEnd), DataRow(null) ] [TestMethod] @@ -31,15 +31,13 @@ public void BitSnackBarPositionTest(BitSnackBarPosition? position) var positionClass = position switch { - BitSnackBarPosition.TopLeft => "bit-snb-tlf", + BitSnackBarPosition.TopStart => "bit-snb-tst", BitSnackBarPosition.TopCenter => "bit-snb-tcn", - BitSnackBarPosition.TopRight => "bit-snb-trt", - - BitSnackBarPosition.BottomLeft => "bit-snb-blf", + BitSnackBarPosition.TopEnd => "bit-snb-ten", + BitSnackBarPosition.BottomStart => "bit-snb-bst", BitSnackBarPosition.BottomCenter => "bit-snb-bcn", - BitSnackBarPosition.BottomRight => "bit-snb-brt", - - _ => "bit-snb-brt", + BitSnackBarPosition.BottomEnd => "bit-snb-ben", + _ => "bit-snb-ben", }; Assert.IsTrue(element.ClassList.Contains(positionClass)); @@ -86,7 +84,7 @@ public async Task BitSnackBarAutoDismissTest(bool autoDismiss) await com.Instance.Show("title"); //Added a sec delay to be sure we are asserting after AutoDismissTime passed - await Task.Delay(com.Instance.AutoDismissTime + TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(4)); var items = com.FindAll(".bit-snb-itm"); @@ -94,21 +92,21 @@ public async Task BitSnackBarAutoDismissTest(bool autoDismiss) } [DataTestMethod, - DataRow("title", BitSnackBarType.Info), - DataRow("title", BitSnackBarType.Warning), - DataRow("title", BitSnackBarType.Success), - DataRow("title", BitSnackBarType.Error), - DataRow("title", BitSnackBarType.SevereWarning), + DataRow("title", BitColor.Info), + DataRow("title", BitColor.Warning), + DataRow("title", BitColor.Success), + DataRow("title", BitColor.Error), + DataRow("title", BitColor.SevereWarning), DataRow("title", null) ] [TestMethod] - public async Task BitSnackBarTypeTest(string title, BitSnackBarType? type) + public async Task BitColorTest(string title, BitColor? color) { var com = RenderComponent<BitSnackBar>(); - if (type.HasValue) + if (color.HasValue) { - await com.Instance.Show(title, type: type.Value); + await com.Instance.Show(title, color: color.Value); } else { @@ -117,23 +115,23 @@ public async Task BitSnackBarTypeTest(string title, BitSnackBarType? type) var element = com.Find(".bit-snb-itm"); - var typeClass = type switch + var colorClass = color switch { - BitSnackBarType.Info => $"bit-snb-info", - BitSnackBarType.Warning => $"bit-snb-warning", - BitSnackBarType.Success => $"bit-snb-success", - BitSnackBarType.Error => $"bit-snb-error", - BitSnackBarType.SevereWarning => $"bit-snb-severe-warning", - _ => string.Empty + BitColor.Info => "bit-snb-inf", + BitColor.Success => "bit-snb-suc", + BitColor.Warning => "bit-snb-wrn", + BitColor.SevereWarning => "bit-snb-swr", + BitColor.Error => "bit-snb-err", + _ => "bit-snb-inf" }; - if (typeClass.IsNullOrEmpty()) + if (colorClass.IsNullOrEmpty()) { Assert.AreEqual(1, element.ClassList.Length); } else { - Assert.IsTrue(element.ClassList.Contains(typeClass)); + Assert.IsTrue(element.ClassList.Contains(colorClass)); } } @@ -222,8 +220,7 @@ public async Task BitSnackBarBodyTemplateTest(string title, string body) var itemTemplateElement = com.Find(".bit-snb-itm"); var expectedHtml = $@"<div diff:ignore></div> - <p>{body}</p> - <span diff:ignore></span>"; + <p>{body}</p>"; itemTemplateElement.InnerHtml.MarkupMatches(expectedHtml); } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/Modal/BitModalTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/Modal/BitModalTests.cs index 3841ea0d2c..9de4770dbf 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/Modal/BitModalTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/Modal/BitModalTests.cs @@ -33,7 +33,7 @@ public void BitModalIsBlockingTest(bool isBlocking) { var com = RenderComponent<BitModal>(parameters => { - parameters.Add(p => p.IsBlocking, isBlocking); + parameters.Add(p => p.Blocking, isBlocking); parameters.Add(p => p.IsOpen, isModalOpen); parameters.Add(p => p.IsOpenChanged, HandleIsOpenChanged); }); @@ -56,7 +56,7 @@ public void BitModalIsModelessTest(bool isModeless) { var com = RenderComponent<BitModal>(parameters => { - parameters.Add(p => p.IsModeless, isModeless); + parameters.Add(p => p.Modeless, isModeless); parameters.Add(p => p.IsOpen, true); }); @@ -149,9 +149,9 @@ public void BitModalContentTest() parameters.AddChildContent("<div>Test Content</div>"); }); - var elementContent = com.Find(".bit-mdl-scn"); + var elementContent = com.Find(".bit-mdl-ctn"); - elementContent.MarkupMatches("<div class=\"bit-mdl-scn\"><div>Test Content</div></div>"); + elementContent.MarkupMatches("<div id:ignore class=\"bit-mdl-ctn\"><div>Test Content</div></div>"); } [TestMethod] @@ -209,33 +209,26 @@ public void BitModalPositionTest(BitModalPosition? position) var com = RenderComponent<BitModal>(parameters => { parameters.Add(p => p.IsOpen, true); - if (position.HasValue) - { - parameters.Add(p => p.Position, position.Value); - } + parameters.Add(p => p.Position, position); }); - var modalElement = com.Find(".bit-mdl-doc"); - var positionClass = position switch { BitModalPosition.Center => "bit-mdl-ctr", - BitModalPosition.TopLeft => "bit-mdl-tl", BitModalPosition.TopCenter => "bit-mdl-tc", BitModalPosition.TopRight => "bit-mdl-tr", - BitModalPosition.CenterLeft => "bit-mdl-cl", BitModalPosition.CenterRight => "bit-mdl-cr", - BitModalPosition.BottomLeft => "bit-mdl-bl", BitModalPosition.BottomCenter => "bit-mdl-bc", BitModalPosition.BottomRight => "bit-mdl-br", - _ => "bit-mdl-ctr", }; - Assert.IsTrue(modalElement.ClassList.Contains(positionClass)); + var element = com.Find(".bit-mdl"); + + Assert.IsTrue(element.ClassList.Contains(positionClass)); } private void HandleIsOpenChanged(bool isOpen) => isModalOpen = isOpen; diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Icon/BitIconTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Icon/BitIconTests.cs index 00b9520c7b..3ac30ce641 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Icon/BitIconTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Icon/BitIconTests.cs @@ -6,7 +6,7 @@ namespace Bit.BlazorUI.Tests.Components.Utilities.Icon; [TestClass] public class BitIconTests : BunitTestContext { - private const string CLASS = "bit-ico bit-ico-pri bit-ico-md"; + private const string CLASS = "bit-ico bit-ico-pri bit-ico-md bit-ico-txt"; [DataTestMethod] public void BitIconShouldRenderExpectedElement() @@ -280,13 +280,13 @@ public void BitIconShouldRespectSize(BitSize? size) var sizeClass = size switch { - BitSize.Small => " bit-ico-sm", - BitSize.Medium => " bit-ico-md", - BitSize.Large => " bit-ico-lg", - _ => " bit-ico-md" + BitSize.Small => "bit-ico-sm", + BitSize.Medium => "bit-ico-md", + BitSize.Large => "bit-ico-lg", + _ => "bit-ico-md" }; - component.MarkupMatches(@$"<i class=""bit-ico bit-ico-pri{sizeClass}"" role=""img"" id:ignore />"); + component.MarkupMatches(@$"<i class=""bit-ico bit-ico-pri bit-ico-txt {sizeClass}"" role=""img"" id:ignore />"); } [DataTestMethod] @@ -301,7 +301,7 @@ public void BitIconShouldRespectSizeChangingAfterRender() parameters.Add(p => p.Size, BitSize.Large); }); - component.MarkupMatches(@"<i class=""bit-ico bit-ico-pri bit-ico-lg"" role=""img"" id:ignore />"); + component.MarkupMatches(@"<i class=""bit-ico bit-ico-pri bit-ico-txt bit-ico-lg"" role=""img"" id:ignore />"); } [DataTestMethod, @@ -335,7 +335,7 @@ public void BitIconShouldRespectColor(BitColor? color) _ => " bit-ico-pri" }; - component.MarkupMatches(@$"<i class=""bit-ico bit-ico-md {colorClass}"" role=""img"" id:ignore />"); + component.MarkupMatches(@$"<i class=""bit-ico bit-ico-md bit-ico-txt {colorClass}"" role=""img"" id:ignore />"); } [DataTestMethod] @@ -350,7 +350,7 @@ public void BitIconShouldRespectColorChangingAfterRender() parameters.Add(p => p.Color, BitColor.Error); }); - component.MarkupMatches(@"<i class=""bit-ico bit-ico-md bit-ico-err"" role=""img"" id:ignore />"); + component.MarkupMatches(@"<i class=""bit-ico bit-ico-md bit-ico-txt bit-ico-err"" role=""img"" id:ignore />"); } [DataTestMethod] diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Link/BitLinkTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Link/BitLinkTests.cs index 10f18e65c6..905bbe19d5 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Link/BitLinkTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Link/BitLinkTests.cs @@ -1,4 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; +using System; using Bunit; namespace Bit.BlazorUI.Tests.Components.Utilities.Link; @@ -599,6 +601,53 @@ public void BitLinkShouldRespectHtmlAttributes(string href) } } + [DataTestMethod, + DataRow(null, null), + DataRow(null, BitAnchorRel.Bookmark), + DataRow(null, BitAnchorRel.Bookmark | BitAnchorRel.Alternate), + DataRow("https://bitplatform.dev", null), + DataRow("https://bitplatform.dev", BitAnchorRel.Bookmark), + DataRow("https://bitplatform.dev", BitAnchorRel.Bookmark | BitAnchorRel.Alternate), + DataRow("#go-to-section", null), + DataRow("#go-to-section", BitAnchorRel.Bookmark), + DataRow("#go-to-section", BitAnchorRel.Bookmark | BitAnchorRel.Alternate) + ] + public void BitLinkShouldRespectTarget(string href, BitAnchorRel? rel) + { + var component = RenderComponent<BitLink>(parameters => + { + parameters.Add(p => p.Rel, rel); + parameters.Add(p => p.Href, href); + }); + + if (href.HasValue()) + { + if (href.StartsWith('#')) + { + component.MarkupMatches(@"<a class=""bit-lnk"" id:ignore></a>"); + } + else + { + if (rel.HasValue) + { + var rels = string.Join(" ", Enum.GetValues(typeof(BitAnchorRel)).Cast<BitAnchorRel>().Where(r => rel.Value.HasFlag(r)).Select(r => r.ToString().ToLower())); + + component.MarkupMatches(@$"<a rel=""{rels}"" href=""{href}"" class=""bit-lnk"" id:ignore></a>"); + } + else + { + component.MarkupMatches(@$"<a class=""bit-lnk"" href=""{href}"" id:ignore></a>"); + } + } + } + else + { + component.MarkupMatches(@"<button class=""bit-lnk"" type=""button"" id:ignore></button>"); + } + } + + + private void MatchSimpleMarkup(IRenderedComponent<BitLink> component, string href) { if (href.HasValue()) diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Separator/BitSeparatorTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Separator/BitSeparatorTests.cs index 34c1816580..5cab330725 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Separator/BitSeparatorTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Separator/BitSeparatorTests.cs @@ -366,7 +366,7 @@ public void BitSeparatorShouldRespectAutoSize(bool autoSize) } [DataTestMethod] - public void BitSeparatorShouldRespectFullWidthChangingAfterRender() + public void BitSeparatorShouldRespectAutoSizeChangingAfterRender() { var component = RenderComponent<BitSeparator>(); @@ -380,6 +380,88 @@ public void BitSeparatorShouldRespectFullWidthChangingAfterRender() component.MarkupMatches(@"<div style=""width:auto"" class=""bit-spr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); } + [DataTestMethod, + DataRow(null), + DataRow(BitColorKind.Primary), + DataRow(BitColorKind.Secondary), + DataRow(BitColorKind.Tertiary), + DataRow(BitColorKind.Transparent) + ] + public void BitSeparatorShouldRespectBackground(BitColorKind? background) + { + var component = RenderComponent<BitSeparator>(parameters => + { + parameters.Add(p => p.Background, background); + }); + + var cssClass = background switch + { + BitColorKind.Primary => "bit-spr-pbg", + BitColorKind.Secondary => "bit-spr-sbg", + BitColorKind.Tertiary => "bit-spr-tbg", + BitColorKind.Transparent => "bit-spr-rbg", + _ => null + }; + + component.MarkupMatches(@$"<div class=""{cssClass} bit-spr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + } + + [DataTestMethod] + public void BitSeparatorShouldRespectBackgroundChangingAfterRender() + { + var component = RenderComponent<BitSeparator>(); + + component.MarkupMatches(@"<div class=""bit-spr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + + component.SetParametersAndRender(parameters => + { + parameters.Add(p => p.Background, BitColorKind.Secondary); + }); + + component.MarkupMatches(@"<div class=""bit-spr bit-spr-sbg bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + } + + [DataTestMethod, + DataRow(null), + DataRow(BitColorKind.Primary), + DataRow(BitColorKind.Secondary), + DataRow(BitColorKind.Tertiary), + DataRow(BitColorKind.Transparent) + ] + public void BitSeparatorShouldRespectBorder(BitColorKind? border) + { + var component = RenderComponent<BitSeparator>(parameters => + { + parameters.Add(p => p.Border, border); + }); + + var cssClass = border switch + { + BitColorKind.Primary => "bit-spr-pbr", + BitColorKind.Secondary => "bit-spr-sbr", + BitColorKind.Tertiary => "bit-spr-tbr", + BitColorKind.Transparent => "bit-spr-rbr", + _ => null + }; + + component.MarkupMatches(@$"<div class=""{cssClass} bit-spr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + } + + [DataTestMethod] + public void BitSeparatorShouldRespectBorderChangingAfterRender() + { + var component = RenderComponent<BitSeparator>(); + + component.MarkupMatches(@"<div class=""bit-spr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + + component.SetParametersAndRender(parameters => + { + parameters.Add(p => p.Border, BitColorKind.Secondary); + }); + + component.MarkupMatches(@"<div class=""bit-spr bit-spr-sbr bit-spr-hrz bit-spr-ctr"" id:ignore></div>"); + } + [DataTestMethod, DataRow(true), DataRow(false) @@ -403,7 +485,7 @@ public void BitSeparatorShouldRespectAutoSizeInVertical(bool autoSize) } [DataTestMethod] - public void BitSeparatorShouldRespectFullWidthChangingAfterRenderInVertical() + public void BitSeparatorShouldRespectAutoSizeChangingAfterRenderInVertical() { var component = RenderComponent<BitSeparator>(parameters => { diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Stack/BitStackTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Stack/BitStackTests.cs index 612beb7857..5a08dba411 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Stack/BitStackTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/Stack/BitStackTests.cs @@ -260,6 +260,37 @@ public void BitStackShouldRespectElement(string element) component.MarkupMatches(@$"<{el} class=""bit-stc"" style=""{STYLE}"" id:ignore></{el}>"); } + [DataTestMethod, + DataRow(true), + DataRow(false) + ] + public void BitStackShouldRespectFillContent(bool fillContent) + { + var component = RenderComponent<BitStack>(parameters => + { + parameters.Add(p => p.FillContent, fillContent); + }); + + var cssClass = fillContent ? " bit-stc-fcn" : null; + + component.MarkupMatches(@$"<div class=""bit-stc{cssClass}"" style=""{STYLE}"" id:ignore></div>"); + } + + [DataTestMethod] + public void BitStackShouldRespectFillContentChangingAfterRender() + { + var component = RenderComponent<BitStack>(); + + component.MarkupMatches(@$"<div style=""{STYLE}"" class=""bit-stc"" id:ignore></div>"); + + component.SetParametersAndRender(parameters => + { + parameters.Add(p => p.FillContent, true); + }); + + component.MarkupMatches(@$"<div class=""bit-stc bit-stc-fcn"" style=""{STYLE}"" id:ignore></div>"); + } + [DataTestMethod, DataRow("10px"), DataRow("1rem"), diff --git a/src/BlazorUI/Bit.BlazorUI/Bit.BlazorUI.csproj b/src/BlazorUI/Bit.BlazorUI/Bit.BlazorUI.csproj index fb047e77a7..9f440dbd75 100644 --- a/src/BlazorUI/Bit.BlazorUI/Bit.BlazorUI.csproj +++ b/src/BlazorUI/Bit.BlazorUI/Bit.BlazorUI.csproj @@ -68,7 +68,7 @@ </Target> <Target Name="BuildCss" Inputs="@(ScssFiles)" Outputs="wwwroot/styles/bit.blazorui.css;wwwroot/styles/bit.blazorui.fluent.css;wwwroot/styles/bit.blazorui.fluent-dark.css;wwwroot/styles/bit.blazorui.fluent-light.css"> - <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.scss:wwwroot/styles/bit.blazorui.css Styles/Fluent/bit.blazorui.fluent.scss:wwwroot/styles/bit.blazorui.fluent.css Styles/Fluent/bit.blazorui.fluent-dark.scss:wwwroot/styles/bit.blazorui.fluent-dark.css Styles/Fluent/bit.blazorui.fluent-light.scss:wwwroot/styles/bit.blazorui.fluent-light.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/bit.blazorui.scss:wwwroot/styles/bit.blazorui.css Styles/Fluent/bit.blazorui.fluent.scss:wwwroot/styles/bit.blazorui.fluent.css Styles/Fluent/bit.blazorui.fluent-dark.scss:wwwroot/styles/bit.blazorui.fluent-dark.css Styles/Fluent/bit.blazorui.fluent-light.scss:wwwroot/styles/bit.blazorui.fluent-light.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitAnchorRel.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitAnchorRel.cs new file mode 100644 index 0000000000..96bd668b78 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/BitAnchorRel.cs @@ -0,0 +1,71 @@ +namespace Bit.BlazorUI; + +[Flags] +public enum BitAnchorRel +{ + /// <summary> + /// Provides a link to an alternate representation of the document. (i.e. print page, translated or mirror) + /// </summary> + Alternate = 1, + + /// <summary> + /// Provides a link to the author of the document. + /// </summary> + Author = 2, + + /// <summary> + /// Permanent URL used for bookmarking. + /// </summary> + Bookmark = 4, + + /// <summary> + /// Indicates that the referenced document is not part of the same site as the current document. + /// </summary> + External = 8, + + /// <summary> + /// Provides a link to a help document. + /// </summary> + Help = 16, + + /// <summary> + /// Provides a link to licensing information for the document. + /// </summary> + License = 32, + + /// <summary> + /// Provides a link to the next document in the series. + /// </summary> + Next = 64, + + /// <summary> + /// Links to an unendorsed document, like a paid link. + /// ("NoFollow" is used by Google, to specify that the Google search spider should not follow that link) + /// </summary> + NoFollow = 128, + + /// <summary> + /// Requires that any browsing context created by following the hyperlink must not have an opener browsing context. + /// </summary> + NoOpener = 256, + + /// <summary> + /// Makes the referrer unknown. No referrer header will be included when the user clicks the hyperlink. + /// </summary> + NoReferrer = 512, + + /// <summary> + /// The previous document in a selection. + /// </summary> + Prev = 1024, + + /// <summary> + /// Links to a search tool for the document. + /// </summary> + Search = 2048, + + /// <summary> + /// A tag (keyword) for the current document. + /// </summary> + Tag = 4096 +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs index 1fa05eabb5..c30cbcd9ff 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs @@ -2,10 +2,9 @@ public abstract partial class BitComponentBase : ComponentBase { + private BitDir? _dir; private string _uniqueId = BitShortId.NewId(); - - protected bool Rendered { get; private set; } internal string _Id => Id ?? _uniqueId; @@ -41,7 +40,12 @@ public abstract partial class BitComponentBase : ComponentBase /// <summary> /// Determines the component direction. /// </summary> - [Parameter] public BitDir? Dir { get; set; } + [Parameter] + public BitDir? Dir + { + get => _dir ?? CascadingDir; + set => _dir = value; + } /// <summary> /// Capture and render additional attributes in addition to the component's parameters. @@ -79,7 +83,9 @@ public override Task SetParametersAsync(ParameterView parameters) switch (parameter.Key) { case nameof(CascadingDir): - CascadingDir = (BitDir?)parameter.Value; + var cascadingDir = (BitDir?)parameter.Value; + if (CascadingDir != cascadingDir) ClassBuilder.Reset(); + CascadingDir = cascadingDir; parametersDictionary.Remove(parameter.Key); break; @@ -153,8 +159,8 @@ protected override void OnInitialized() ClassBuilder .Register(() => RootElementClass) - .Register(() => (IsEnabled ? string.Empty : "bit-dis")) - .Register(() => (Dir == BitDir.Rtl ? "bit-rtl" : string.Empty)); + .Register(() => IsEnabled ? string.Empty : "bit-dis") + .Register(() => Dir == BitDir.Rtl ? "bit-rtl" : string.Empty); RegisterCssClasses(); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitTextAlign.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitTextAlign.cs new file mode 100644 index 0000000000..310a0c3859 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/BitTextAlign.cs @@ -0,0 +1,18 @@ +namespace Bit.BlazorUI; + +public enum BitTextAlign +{ + Start, + End, + Left, + Right, + Center, + Justify, + JustifyAll, + MatchParent, + Inherit, + Initial, + Revert, + RevertLayer, + Unset +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor index ae6caa572f..ff0059f783 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor @@ -19,7 +19,7 @@ { <i style="@Styles?.Icon" class="bit-acb-ico bit-icon bit-icon--@IconName @Classes?.Icon" /> } - @if (ChildContent is not null) + @if (ChildContent is not null && IconOnly is false) { <div style="@Styles?.Content" class="bit-acb-con @Classes?.Content"> @ChildContent diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor.cs index 8c79a20f8d..19b76ed6c1 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.razor.cs @@ -52,6 +52,12 @@ public partial class BitActionButton : BitComponentBase [Parameter, ResetClassBuilder] public BitColor? Color { get; set; } + /// <summary> + /// Renders the action button in full width of its container with flex-start. + /// </summary> + [Parameter, ResetClassBuilder] + public bool FullWidth { get; set; } + /// <summary> /// The value of the href attribute of the link rendered by the button. /// If provided, the component will be rendered as an anchor tag instead of button. @@ -63,6 +69,11 @@ public partial class BitActionButton : BitComponentBase /// </summary> [Parameter] public string? IconName { get; set; } + /// <summary> + /// Removes the container of the text and only renders the icon. + /// </summary> + [Parameter] public bool IconOnly { get; set; } + /// <summary> /// The callback for the click event of the button. /// </summary> @@ -124,6 +135,8 @@ protected override void RegisterCssClasses() _ => "bit-acb-pri" }); + ClassBuilder.Register(() => FullWidth ? "bit-acb-fwi" : string.Empty); + ClassBuilder.Register(() => Size switch { BitSize.Small => "bit-acb-sm", diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.scss index 9530138fe5..aba9eee7f8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitActionButton/BitActionButton.scss @@ -41,12 +41,19 @@ } .bit-acb-con { + flex-grow: 1; + display: flex; } .bit-acb-rvi { flex-direction: row-reverse; } +.bit-acb-fwi { + width: 100%; + justify-content: flex-start; +} + .bit-acb-pri { --bit-acb-clr-ico: #{$clr-pri}; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitButtonGroup/BitButtonGroup.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitButtonGroup/BitButtonGroup.razor.cs index 692db5f1c8..150b45e63e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitButtonGroup/BitButtonGroup.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitButtonGroup/BitButtonGroup.razor.cs @@ -19,7 +19,9 @@ public partial class BitButtonGroup<TItem> : BitComponentBase where TItem : clas /// <summary> /// The content of the BitButtonGroup, that are BitButtonGroupOption components. /// </summary> - [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetChildContentAndItems))] + public RenderFragment? ChildContent { get; set; } /// <summary> /// Defines the general colors available in the bit BlazorUI. @@ -30,7 +32,9 @@ public partial class BitButtonGroup<TItem> : BitComponentBase where TItem : clas /// <summary> /// List of Item, each of which can be a button with different action in the ButtonGroup. /// </summary> - [Parameter] public IEnumerable<TItem> Items { get; set; } = []; + [Parameter] + [CallOnSet(nameof(OnSetChildContentAndItems))] + public IEnumerable<TItem> Items { get; set; } = []; /// <summary> /// The content inside the item can be customized. @@ -134,17 +138,6 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => Vertical ? "bit-btg-vrt" : ""); } - protected override Task OnParametersSetAsync() - { - if (ChildContent is null && Items.Any() && Items != _oldItems) - { - _oldItems = Items; - _items = Items.ToList(); - } - - return base.OnParametersSetAsync(); - } - private async Task HandleOnItemClick(TItem item) @@ -176,6 +169,15 @@ private async Task HandleOnItemClick(TItem item) } } + private void OnSetChildContentAndItems() + { + if (ChildContent is not null) return; + if (Items.Any() is false || Items == _oldItems) return; + + _oldItems = Items; + _items = Items.ToList(); + } + private string? GetClass(TItem? item) { if (item is null) return null; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor index ccc09966a5..90ff2c90e3 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor @@ -65,7 +65,7 @@ @if (Split is false) { - <i style="@Styles?.ChevronDown" class="bit-icon bit-icon--@ChevronDownIcon @Classes?.ChevronDown" /> + <i style="@Styles?.ChevronDown" class="bit-icon bit-icon--@(ChevronDownIcon ?? "ChevronRight bit-ico-r90") @Classes?.ChevronDown" /> } </button> @@ -78,7 +78,7 @@ tabindex="@(IsEnabled ? 0 : -1)" style="@Styles?.ChevronDownButton" class="bit-mnb-chb @Classes?.ChevronDownButton"> - <i style="@Styles?.ChevronDown" class="bit-icon bit-icon--@ChevronDownIcon @Classes?.ChevronDown" /> + <i style="@Styles?.ChevronDown" class="bit-icon bit-icon--@(ChevronDownIcon ?? "ChevronRight bit-ico-r90") @Classes?.ChevronDown" /> </button> } @@ -89,14 +89,14 @@ <div id="@_calloutId" style="@Styles?.Callout" class="bit-mnb-cal @Classes?.Callout"> - <ul role="presentation" class="bit-mnb-cul"> + <ul role="presentation" style="@Styles?.CalloutContainer" class="bit-mnb-cul @Classes?.CalloutContainer"> @foreach (TItem item in _items) { if (Sticky && item == SelectedItem) continue; var template = GetTemplate(item); var isEnabled = GetIsEnabled(item); - <li role="presentation" @key="GetKey(item)"> + <li role="presentation" @key="GetKey(item)" style="@Styles?.ItemWrapper" class="@Classes?.ItemWrapper"> <button @onclick="() => HandleOnItemClick(item)" @onclick:stopPropagation role="menuitem" type="@_buttonType.GetValue()" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor.cs index 0fe9614dcd..700f944d09 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButton.razor.cs @@ -42,7 +42,7 @@ public partial class BitMenuButton<TItem> : BitComponentBase, IAsyncDisposable w /// <summary> /// The icon name of the chevron down part of the menu button. /// </summary> - [Parameter] public string ChevronDownIcon { get; set; } = "ChevronDown"; + [Parameter] public string? ChevronDownIcon { get; set; } /// <summary> /// The content of the menu button, that are BitMenuButtonOption components. diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButtonClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButtonClassStyles.cs index 2695b5da22..800ae295e1 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButtonClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/BitMenuButton/BitMenuButtonClassStyles.cs @@ -28,10 +28,15 @@ public class BitMenuButtonClassStyles public string? OperatorButtonText { get; set; } /// <summary> - /// Custom CSS classes/styles for the callout container of the BitMenuButton. + /// Custom CSS classes/styles for the callout of the BitMenuButton. /// </summary> public string? Callout { get; set; } + /// <summary> + /// Custom CSS classes/styles for the callout container of the BitMenuButton. + /// </summary> + public string? CalloutContainer { get; set; } + /// <summary> /// Custom CSS classes/styles for the chevron down button of the BitMenuButton. /// </summary> @@ -52,6 +57,11 @@ public class BitMenuButtonClassStyles /// </summary> public string? Icon { get; set; } + /// <summary> + /// Custom CSS classes/styles for each item wrapper of the BitMenuButton. + /// </summary> + public string? ItemWrapper { get; set; } + /// <summary> /// Custom CSS classes/styles for each item of the BitMenuButton. /// </summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputBase.cs index e709c7ba73..ce37361a0f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputBase.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputBase.cs @@ -55,7 +55,7 @@ protected BitInputBase() /// <summary> /// Gets or sets a collection of additional attributes that will be applied to the created element. /// </summary> - [Parameter] public IReadOnlyDictionary<string, object>? InputHtmlAttributes { get; set; } + [Parameter] public Dictionary<string, object>? InputHtmlAttributes { get; set; } /// <summary> /// Gets or sets the name of the element. @@ -152,7 +152,7 @@ public override Task SetParametersAsync(ParameterView parameters) break; case nameof(InputHtmlAttributes): - InputHtmlAttributes = (IReadOnlyDictionary<string, object>?)parameter.Value; + InputHtmlAttributes = (Dictionary<string, object>?)parameter.Value; parametersDictionary.Remove(parameter.Key); break; @@ -359,7 +359,7 @@ protected async Task SetCurrentValueAsStringAsync(string? value, bool bypass = f protected async Task SetCurrentValueAsync(TValue? value) { - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (InvalidValueBinding()) return; Value = value; @@ -370,6 +370,13 @@ protected async Task SetCurrentValueAsync(TValue? value) await OnChange.InvokeAsync(value); } + protected bool InvalidValueBinding() + { + return (ValueHasBeenSet && + ValueChanged.HasDelegate is false && + OnChange.HasDelegate is false); + } + private void OnValidateStateChanged(object? sender, ValidationStateChangedEventArgs eventArgs) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputMode.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputMode.cs new file mode 100644 index 0000000000..6e7643c223 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputMode.cs @@ -0,0 +1,48 @@ +namespace Bit.BlazorUI; + +/// <summary> +/// This allows a browser to display an appropriate virtual keyboard. +/// <see href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode"/> +/// </summary> +public enum BitInputMode +{ + /// <summary> + /// No virtual keyboard. For when the page implements its own keyboard input control. + /// </summary> + None, + + /// <summary> + /// Standard input keyboard for the user's current locale. + /// </summary> + Text, + + /// <summary> + /// Fractional numeric input keyboard containing the digits and decimal separator for the user's locale. + /// </summary> + Decimal, + + /// <summary> + /// Numeric input keyboard, but only requires the digits 0–9. + /// </summary> + Numeric, + + /// <summary> + /// A telephone keypad input, including the digits 0–9, the asterisk (*), and the pound (#) key + /// </summary> + Tel, + + /// <summary> + /// A virtual keyboard optimized for search input. + /// </summary> + Search, + + /// <summary> + /// A virtual keyboard optimized for entering email addresses. + /// </summary> + Email, + + /// <summary> + /// A keypad optimized for entering URLs. + /// </summary> + Url +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitTextInputBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitTextInputBase.cs index da041c08de..b4c4dc61b5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitTextInputBase.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitTextInputBase.cs @@ -11,6 +11,15 @@ public abstract class BitTextInputBase<TValue> : BitInputBase<TValue> private ChangeEventArgs _lastThrottleEventArgs = default!; + /// <summary> + /// Specifies the value of the autocomplete attribute of the input component. + /// </summary> + [Parameter] public string? AutoComplete { get; set; } + + /// <summary> + /// Determines if the text input is auto focused on first render. + /// </summary> + [Parameter] public bool AutoFocus { get; set; } /// <summary> /// The debounce time in milliseconds. @@ -37,6 +46,16 @@ public override Task SetParametersAsync(ParameterView parameters) { switch (parameter.Key) { + case nameof(AutoComplete): + AutoComplete = (string?)parameter.Value; + parametersDictionary.Remove(parameter.Key); + break; + + case nameof(AutoFocus): + AutoFocus = (bool)parameter.Value; + parametersDictionary.Remove(parameter.Key); + break; + case nameof(DebounceTime): DebounceTime = (int)parameter.Value; parametersDictionary.Remove(parameter.Key); @@ -57,6 +76,18 @@ public override Task SetParametersAsync(ParameterView parameters) return base.SetParametersAsync(ParameterView.FromDictionary(parametersDictionary!)); } + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender is false || IsEnabled is false) return; + + if (AutoFocus) + { + await InputElement.FocusAsync(); + } + } + /// <summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor index 67ef8bd333..a5284de16c 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor @@ -451,7 +451,7 @@ aria-disabled="@(IsEnabled is false)" style="@Styles?.TimePickerIncreaseHourButton" class="bit-cal-tbt @Classes?.TimePickerIncreaseHourButton"> - <i style="@Styles?.TimePickerIncreaseHourIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.TimePickerIncreaseHourIcon" aria-hidden="true" /> + <i style="@Styles?.TimePickerIncreaseHourIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.TimePickerIncreaseHourIcon" aria-hidden="true" /> </button> <input @ref="_inputTimeHourRef" @bind="@_hourView" @@ -487,7 +487,7 @@ aria-disabled="@(IsEnabled is false)" style="@Styles?.TimePickerIncreaseMinuteButton" class="bit-cal-tbt @Classes?.TimePickerIncreaseMinuteButton"> - <i style="@Styles?.TimePickerIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.TimePickerIncreaseMinuteIcon" aria-hidden="true" /> + <i style="@Styles?.TimePickerIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.TimePickerIncreaseMinuteIcon" aria-hidden="true" /> </button> <input @ref="_inputTimeMinuteRef" @bind="@_minuteView" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs index 2e89cf958d..651bbad092 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.razor.cs @@ -107,7 +107,7 @@ private int _minuteView /// CultureInfo for the Calendar. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public CultureInfo? Culture { get; set; } /// <summary> @@ -189,21 +189,21 @@ private int _minuteView /// Whether the month picker is shown or hidden. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowMonthPicker { get; set; } = true; /// <summary> /// The maximum allowable date of the calendar. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MaxDate { get; set; } /// <summary> /// The minimum allowable date of the calendar. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MinDate { get; set; } /// <summary> @@ -245,7 +245,7 @@ private int _minuteView /// Whether the time picker should be shown or not. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowTimePicker { get; set; } /// <summary> @@ -302,7 +302,7 @@ private int _minuteView /// Specifies the date and time of the calendar when it is showing without any selected value. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? StartingValue { get; set; } @@ -323,12 +323,9 @@ protected override void RegisterCssStyles() protected override void OnInitialized() { - _showTimePicker = ShowTimePicker && ShowTimePickerAsOverlay is false; - _showMonthPicker = _showTimePicker is false && ShowMonthPicker && ShowMonthPickerAsOverlay is false; - OnValueChanged += HandleOnValueChanged; - HandleParameterChanges(); + OnSetParameters(); base.OnInitialized(); } @@ -376,13 +373,13 @@ protected override void Dispose(bool disposing) private void HandleOnValueChanged(object? sender, EventArgs args) { - HandleParameterChanges(); + OnSetParameters(); } - private void HandleParameterChanges() + private void OnSetParameters() { - _showTimePicker = ShowTimePicker; - _showMonthPicker = ShowMonthPicker; + _showTimePicker = ShowTimePicker && ShowTimePickerAsOverlay is false; + _showMonthPicker = _showTimePicker is false && ShowMonthPicker && ShowMonthPickerAsOverlay is false; _culture = Culture ?? CultureInfo.CurrentUICulture; @@ -406,8 +403,7 @@ private void HandleParameterChanges() private void SelectDate(int dayIndex, int weekIndex) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (IsWeekDayOutOfMinAndMaxDate(dayIndex, weekIndex)) return; _currentDay = _daysOfCurrentMonth[weekIndex, dayIndex]; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss index 6bbe3c4de9..f7fcfeeb6a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss @@ -510,4 +510,4 @@ padding: spacing(1.5); box-sizing: content-box; flex-flow: column nowrap; -} +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs index 1d3e4692e4..bd6b85bed5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs @@ -19,7 +19,9 @@ public partial class BitChoiceGroup<TItem, TValue> : BitInputBase<TValue> where /// <summary> /// The content of the ChoiceGroup, a list of BitChoiceGroupOption components. /// </summary> - [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public RenderFragment? ChildContent { get; set; } /// <summary> /// Custom CSS classes for different parts of the BitChoiceGroup. @@ -29,17 +31,26 @@ public partial class BitChoiceGroup<TItem, TValue> : BitInputBase<TValue> where /// <summary> /// Default selected item for ChoiceGroup. /// </summary> - [Parameter] public TValue? DefaultValue { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public TValue? DefaultValue { get; set; } /// <summary> /// Renders the items in the ChoiceGroup horizontally. /// </summary> [Parameter] public bool Horizontal { get; set; } + /// <summary> + /// Renders the icons and images in a single line with the items in the ChoiceGroup. + /// </summary> + [Parameter] public bool Inline { get; set; } + /// <summary> /// Sets the data source that populates the items of the list. /// </summary> - [Parameter] public IEnumerable<TItem> Items { get; set; } = []; + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public IEnumerable<TItem> Items { get; set; } = []; /// <summary> /// Used to customize the label for the Item Label content. @@ -71,6 +82,12 @@ public partial class BitChoiceGroup<TItem, TValue> : BitInputBase<TValue> where /// </summary> [Parameter] public BitChoiceGroupNameSelectors<TItem, TValue>? NameSelectors { get; set; } + /// <summary> + /// Removes the circle from the start of each item. + /// </summary> + [Parameter, ResetClassBuilder] + public bool NoCircle { get; set; } + /// <summary> /// Callback for when the option clicked. /// </summary> @@ -115,27 +132,16 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); } - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - - if (ChildContent is not null || Items.Any() is false || Items == _oldItems) return; - - _oldItems = Items; - _items = Items.ToList(); - - if (ValueHasBeenSet is false && DefaultValue is not null && _items.Any(item => EqualityComparer<TValue>.Default.Equals(GetValue(item), DefaultValue))) - { - Value = DefaultValue; - } - } - protected override string RootElementClass => "bit-chg"; protected override void RegisterCssClasses() { ClassBuilder.Register(() => Classes?.Root); + ClassBuilder.Register(() => Inline ? "bit-chg-inl" : string.Empty); + + ClassBuilder.Register(() => NoCircle ? "bit-chg-ncr" : "bit-chg-wcr"); + ClassBuilder.Register(() => IsEnabled && Required ? "bit-chg-req" : string.Empty); } @@ -149,12 +155,35 @@ protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(fa + private void OnSetParameters() + { + if (ChildContent is not null) return; + if (Items.Any() is false || Items == _oldItems) return; + + _oldItems = Items; + _items = Items.ToList(); + + InitDefaultValue(); + } + private void InitDefaultValue() { - if (ValueHasBeenSet is false && DefaultValue is not null && - _items.Any(item => EqualityComparer<TValue>.Default.Equals(GetValue(item), DefaultValue))) + if (ValueHasBeenSet) + { + var item = _items.FirstOrDefault(item => EqualityComparer<TValue>.Default.Equals(GetValue(item), Value)); + if (item is not null) + { + SetIsSelectedForSelectedItem(item); + } + } + else if (DefaultValue is not null) { - Value = DefaultValue; + var item = _items.FirstOrDefault(item => EqualityComparer<TValue>.Default.Equals(GetValue(item), DefaultValue)); + if (item is not null) + { + SetIsSelectedForSelectedItem(item); + Value = DefaultValue; + } } } @@ -187,6 +216,8 @@ private async Task HandleChange(TItem item) { if (IsEnabled is false || GetIsEnabled(item) is false) return; + SetIsSelectedForSelectedItem(item); + CurrentValue = GetValue(item); } @@ -216,8 +247,7 @@ private string GetItemContainerCssStyles(TItem item) private string GetItemContainerCssClasses(TItem item) { - StringBuilder cssClass = new(RootElementClass); - cssClass.Append("-icn"); + StringBuilder cssClass = new("bit-chg-icn"); if (string.IsNullOrEmpty(GetClass(item)) is false) { @@ -233,10 +263,7 @@ private string GetItemContainerCssClasses(TItem item) if (GetIsCheckedItem(item)) { - cssClass.Append(' ') - .Append(RootElementClass) - .Append("-ich"); - + cssClass.Append(' ').Append("bit-chg-ich"); cssClass.Append(' ').Append(Classes?.ItemChecked); } @@ -244,16 +271,12 @@ private string GetItemContainerCssClasses(TItem item) if (IsEnabled is false || GetIsEnabled(item) is false) { - cssClass.Append(' ') - .Append(RootElementClass) - .Append("-ids"); + cssClass.Append(' ').Append("bit-chg-ids"); } if (GetImageSrc(item).HasValue() || GetIconName(item).HasValue()) { - cssClass.Append(' ') - .Append(RootElementClass) - .Append("-ihi"); + cssClass.Append(' ').Append("bit-chg-ihi"); } return cssClass.ToString(); @@ -262,7 +285,7 @@ private string GetItemContainerCssClasses(TItem item) private string GetItemLabelWrapperCssClasses(TItem item) { var hasImageOrIcon = GetImageSrc(item).HasValue() || GetIconName(item).HasValue(); - return hasImageOrIcon && ItemLabelTemplate is null + return hasImageOrIcon && ItemLabelTemplate is null && Inline is false ? "bit-chg-ilwi" : "bit-chg-ilw"; } @@ -591,4 +614,31 @@ private void SetIndex(TItem item, int value) item.SetValueToProperty(NameSelectors.Index, value); } + + private void SetIsSelected(TItem item, bool value) + { + if (item is BitChoiceGroupItem<TValue> choiceGroupItem) + { + choiceGroupItem.IsSelected = value; + } + + if (item is BitChoiceGroupOption<TValue> choiceGroupOption) + { + choiceGroupOption.IsSelected = value; + } + + if (NameSelectors is null) return; + + item.SetValueToProperty(NameSelectors.IsSelected, value); + } + + private void SetIsSelectedForSelectedItem(TItem item) + { + foreach (var itm in _items) + { + SetIsSelected(itm, false); + } + + SetIsSelected(item, true); + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss index 3a0a7b5066..2f7da0756a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss @@ -1,8 +1,12 @@ @import "../../../Styles/functions.scss"; .bit-chg { - display: block; + display: flex; + gap: spacing(1); font-weight: 400; + width: fit-content; + padding: spacing(1); + flex-direction: column; font-size: spacing(1.75); font-family: $tg-font-family; @@ -81,19 +85,18 @@ } .bit-chg-lbl { - margin: 0; display: block; box-shadow: none; font-weight: 600; + color: $clr-fg-pri; box-sizing: border-box; font-size: spacing(1.75); - padding: spacing(0.625) 0; overflow-wrap: break-word; - color: $clr-fg-pri; } .bit-chg-cnt { display: flex; + gap: spacing(1); flex-flow: column nowrap; } @@ -106,13 +109,11 @@ display: flex; font-weight: 400; position: relative; + color: $clr-fg-pri; align-items: center; box-sizing: border-box; font-size: spacing(1.75); min-height: spacing(3.25); - margin-inline-end: spacing(1); - margin-block-start: spacing(1); - color: $clr-fg-pri; font-family: $tg-font-family; } @@ -133,53 +134,12 @@ margin-block-start: 0; display: inline-block; min-height: spacing(2.5); - padding-inline-start: spacing(3.25); - - &:before { - content: ""; - position: absolute; - border-radius: 50%; - width: spacing(2.5); - height: spacing(2.5); - inset-block-start: 0; - inset-inline-start: 0; - display: inline-block; - box-sizing: border-box; - background-color: $clr-bg-pri; - transition: border-color 0.2s cubic-bezier(0.4, 0, 0.23, 1); - border: $shp-border-width $shp-border-style $clr-brd-pri; - } - - &:after { - width: 0; - height: 0; - content: ""; - position: absolute; - border-radius: 50%; - inset-inline-end: 0; - box-sizing: border-box; - inset-inline-start: spacing(1.25); - transition: border-width 0.2s cubic-bezier(0.4, 0, 0.23, 1); - } @media (hover: hover) { &:hover { .bit-chg-itx { color: $clr-fg-pri-hover; } - - &:before { - border-color: $clr-brd-pri-hover; - } - - &:after { - width: spacing(1.25); - height: spacing(1.25); - inset-block-start: spacing(0.625); - inset-inline-start: spacing(0.625); - transition-property: background-color; - background-color: $clr-fg-sec-hover; - } } } } @@ -201,42 +161,129 @@ padding-block-start: spacing(2.75); border: $shp-border-width $shp-border-style transparent; - &:before { - opacity: 0; - content: ""; - position: absolute; - border-radius: 50%; - width: spacing(2.5); - height: spacing(2.5); - display: inline-block; - box-sizing: border-box; - inset-inline-start: auto; - inset-inline-end: spacing(0.375); - inset-block-start: spacing(0.375); - background-color: $clr-bg-pri; - transition: border-color 0.2s cubic-bezier(0.4, 0, 0.23, 1); - border: $shp-border-width $shp-border-style $clr-brd-pri; + @media (hover: hover) { + &:hover { + border-color: $clr-brd-pri-hover; + } } +} + +.bit-chg-ncr { +} + +.bit-chg-wcr { + .bit-chg-ilw { + padding-inline-start: spacing(3.25); + + &:before { + top: 50%; + content: ""; + position: absolute; + border-radius: 50%; + width: spacing(2.5); + height: spacing(2.5); + display: inline-block; + inset-inline-start: 0; + box-sizing: border-box; + transform: translateY(-50%); + background-color: $clr-bg-pri; + transition: border-color 0.2s cubic-bezier(0.4, 0, 0.23, 1); + border: $shp-border-width $shp-border-style $clr-brd-pri; + } + + &:after { + top: 50%; + opacity: 0; + content: ""; + position: absolute; + border-radius: 50%; + width: spacing(1.25); + height: spacing(1.25); + box-sizing: border-box; + transform: translateY(-50%); + inset-inline-start: spacing(0.625); + background-color: $clr-fg-sec-hover; + transition-property: background-color; + transition: border-width 0.2s cubic-bezier(0.4, 0, 0.23, 1); + } - &:after { - width: 0; - height: 0; - content: ""; - position: absolute; - border-radius: 50%; - inset-inline-end: 0; - box-sizing: border-box; - inset-inline-start: spacing(1.25); - transition: border-width 0.2s cubic-bezier(0.4, 0, 0.23, 1); + @media (hover: hover) { + &:hover { + &:before { + border-color: $clr-brd-pri-hover; + } + + &:after { + opacity: 1; + } + } + } } - @media (hover: hover) { - &:hover { - border-color: $clr-brd-pri-hover; + .bit-chg-ilwi { + &:before { + opacity: 0; + content: ""; + position: absolute; + border-radius: 50%; + width: spacing(2.5); + height: spacing(2.5); + display: inline-block; + box-sizing: border-box; + inset-inline-start: auto; + background-color: $clr-bg-pri; + inset-inline-end: spacing(0.375); + inset-block-start: spacing(0.375); + transition: border-color 0.2s cubic-bezier(0.4, 0, 0.23, 1); + border: $shp-border-width $shp-border-style $clr-brd-pri; + } - &:before { - opacity: 1; - border-color: $clr-brd-pri-hover; + &:after { + width: 0; + height: 0; + content: ""; + position: absolute; + border-radius: 50%; + inset-inline-end: 0; + box-sizing: border-box; + inset-inline-start: spacing(1.25); + transition: border-width 0.2s cubic-bezier(0.4, 0, 0.23, 1); + } + + @media (hover: hover) { + &:hover { + &:before { + opacity: 1; + border-color: $clr-brd-pri-hover; + } + } + } + } +} + +.bit-chg-inl { + .bit-chg-ihi { + background-color: unset; + + .bit-chg-ilw { + display: flex; + gap: spacing(1); + align-items: center; + justify-content: center; + + .bit-chg-iic, + .bit-chg-icc { + height: unset; + padding: unset; + + .bit-chg-icw { + font-size: spacing(2); + } + } + + .bit-chg-itw { + margin: unset; + height: unset; } } } @@ -314,10 +361,7 @@ } &:after { - width: spacing(1.25); - height: spacing(1.25); - inset-block-start: spacing(0.625); - inset-inline-start: spacing(0.625); + opacity: 1; background-color: $clr-pri; border: spacing(0.625) $shp-border-style $clr-pri; } @@ -370,6 +414,10 @@ } } } + + .bit-chg-itx { + font-weight: bold; + } } .bit-chg-ids { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupItem.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupItem.cs index 70197c59c5..1933a56c47 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupItem.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupItem.cs @@ -78,4 +78,9 @@ public class BitChoiceGroupItem<TValue> /// Index of the BitChoiceGroup item. This property's value is set by the component at render. /// </summary> public int Index { get; internal set; } + + /// <summary> + /// Determines if the item is selected. This property's value is assigned by the component. + /// </summary> + public bool IsSelected { get; internal set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupNameSelectors.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupNameSelectors.cs index a9ed00e4e8..d0bdaf3037 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupNameSelectors.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupNameSelectors.cs @@ -78,4 +78,9 @@ public class BitChoiceGroupNameSelectors<TItem, TValue> /// The Index field name of the custom input class. This property's value is set by the component at render. /// </summary> public string Index { get; set; } = nameof(BitChoiceGroupItem<TValue>.Index); + + /// <summary> + /// The IsSelected field name of the custom input class. This property's value is assigned by the component. + /// </summary> + public string IsSelected { get; set; } = nameof(BitChoiceGroupItem<TValue>.IsSelected); } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs index 538097de1b..ffdc8df3a9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs @@ -83,6 +83,11 @@ public partial class BitChoiceGroupOption<TValue> : ComponentBase, IDisposable /// </summary> public int Index { get; internal set; } + /// <summary> + /// Determines if the option is selected. This property's value is assigned by the component. + /// </summary> + public bool IsSelected { get; internal set; } + protected override async Task OnInitializedAsync() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor index 449e85d59d..0dbac335eb 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor @@ -133,7 +133,7 @@ { <i aria-hidden="true" style="@Styles?.CaretDownIcon" - class="bit-icon bit-icon--@CaretDownIconName @Classes?.CaretDownIcon" /> + class="bit-icon bit-icon--@(CaretDownIconName ?? "ChevronRight bit-ico-r90") @Classes?.CaretDownIcon" /> } </span> @@ -162,7 +162,7 @@ class="bit-drp-cal @Classes?.Callout" aria-labelledby="@_labelId"> - @if (IsResponsive) + @if (Responsive) { <div style="@Styles?.ResponsiveLabelContainer" class="bit-drp-rlc @Classes?.ResponsiveLabelContainer"> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs index 8d843136d7..a60c48ab2a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs @@ -50,9 +50,9 @@ namespace Bit.BlazorUI; [Parameter] public RenderFragment? CalloutFooterTemplate { get; set; } /// <summary> - /// The icon name of the chevron down element of the dropdown. The default value is ChevronDown. + /// The icon name of the chevron down element of the dropdown. /// </summary> - [Parameter] public string CaretDownIconName { get; set; } = "ChevronDown"; + [Parameter] public string? CaretDownIconName { get; set; } /// <summary> /// The custom template for the chevron down element of the dropdown. @@ -81,14 +81,14 @@ namespace Bit.BlazorUI; [Parameter] public bool Combo { get; set; } /// <summary> - /// The default key value that will be initially used to set selected item if the Value parameter is not set. + /// The default value that will be initially used to set selected item if the Value parameter is not set. /// </summary> [Parameter] public TValue? DefaultValue { get; set; } /// <summary> - /// The default key value that will be initially used to set selected items in multi select mode if the Values parameter is not set. + /// The default values that will be initially used to set selected items in multi select mode if the Values parameter is not set. /// </summary> - [Parameter] public ICollection<TValue?>? DefaultValues { get; set; } + [Parameter] public IEnumerable<TValue?>? DefaultValues { get; set; } /// <summary> /// Determines the allowed drop directions of the callout. @@ -126,11 +126,6 @@ namespace Bit.BlazorUI; /// </summary> [Parameter] public RenderFragment<TItem>? HeaderTemplate { get; set; } - /// <summary> - /// Enables the multi select mode. - /// </summary> - [Parameter] public bool IsMultiSelect { get; set; } - /// <summary> /// Determines the opening state of the callout. (two-way bound) /// </summary> @@ -138,16 +133,6 @@ namespace Bit.BlazorUI; [CallOnSet(nameof(ClearSearchBox))] public bool IsOpen { get; set; } - /// <summary> - /// Enables calling the select events when the same item is selected in single select mode. - /// </summary> - [Parameter] public bool IsReselectable { get; set; } - - /// <summary> - /// Enables the responsive mode of the component for small screens. - /// </summary> - [Parameter] public bool IsResponsive { get; set; } - /// <summary> /// The list of items to display in the callout. /// </summary> @@ -178,6 +163,11 @@ namespace Bit.BlazorUI; /// </summary> [Parameter] public RenderFragment? LabelTemplate { get; set; } + /// <summary> + /// Enables the multi select mode. + /// </summary> + [Parameter] public bool MultiSelect { get; set; } + /// <summary> /// The delimiter for joining the values to create the text of the dropdown in multi select mode. /// </summary> @@ -217,7 +207,7 @@ namespace Bit.BlazorUI; /// <summary> /// The callback that called when selected items change. /// </summary> - [Parameter] public EventCallback<TItem[]> OnValuesChange { get; set; } + [Parameter] public EventCallback<IEnumerable<TValue>> OnValuesChange { get; set; } /// <summary> /// Alias of ChildContent. @@ -255,6 +245,16 @@ namespace Bit.BlazorUI; /// </summary> [Parameter] public bool PreserveCalloutWidth { get; set; } + /// <summary> + /// Enables calling the select events when the same item is selected in single select mode. + /// </summary> + [Parameter] public bool Reselectable { get; set; } + + /// <summary> + /// Enables the responsive mode of the component for small screens. + /// </summary> + [Parameter] public bool Responsive { get; set; } + /// <summary> /// The placeholder text of the SearchBox input. /// </summary> @@ -312,9 +312,9 @@ namespace Bit.BlazorUI; /// </summary> [Parameter, TwoWayBound] [CallOnSet(nameof(OnSetValues))] - public ICollection<TValue?>? Values { get; set; } + public IEnumerable<TValue?>? Values { get; set; } - [Parameter] public Expression<Func<ICollection<TValue?>?>>? ValuesExpression { get; set; } + [Parameter] public Expression<Func<IEnumerable<TValue?>?>>? ValuesExpression { get; set; } /// <summary> /// Enables virtualization to render only the visible items. @@ -331,12 +331,12 @@ namespace Bit.BlazorUI; /// <summary> /// A readonly list of the current selected items in multi-select mode. /// </summary> - public IReadOnlyList<TItem> SelectedItems => IsMultiSelect ? _selectedItems : []; + public IReadOnlyList<TItem> SelectedItems => MultiSelect ? _selectedItems : []; /// <summary> /// The current selected item in single-select mode. /// </summary> - public TItem? SelectedItem => IsMultiSelect ? default : _selectedItems.FirstOrDefault(); + public TItem? SelectedItem => MultiSelect ? default : _selectedItems.FirstOrDefault(); /// <summary> /// The ElementReference to the combo input element. @@ -382,7 +382,7 @@ public async Task UnselectItem(TItem? item) { if (item is null) return; - if (IsMultiSelect) + if (MultiSelect) { await HandleOnItemClick(item); } @@ -392,9 +392,11 @@ public async Task UnselectItem(TItem? item) } } + + internal void RegisterOption(BitDropdownOption<TValue> option) { - Items.Add((option as TItem)!); + Items!.Add((option as TItem)!); UpdateSelectedItemsFromValues(); @@ -404,11 +406,12 @@ internal void RegisterOption(BitDropdownOption<TValue> option) internal void UnregisterOption(BitDropdownOption<TValue> option) { var item = (option as TItem)!; - Items.Remove(item); + Items!.Remove(item); if (_selectedItems.Contains(item)) { _selectedItems = _selectedItems.FindAll(i => i != item); + SetIsSelectedForSelectedItems(); } StateHasChanged(); @@ -425,70 +428,6 @@ internal async Task HandleOnItemClick(TItem item) StateHasChanged(); } - private async Task AddOrRemoveSelectedItem(TItem? item, bool addDynamic = false) - { - if (item is null) return; - - if (IsMultiSelect) - { - if (ValuesHasBeenSet && ValuesChanged.HasDelegate is false) return; - - var isSelected = GetIsSelected(item) is false; - - var tempValue = Values?.ToList() ?? []; - - if (isSelected) - { - tempValue.Add(GetValue(item)); - } - else - { - tempValue.Remove(GetValue(item)); - } - - await AssignValues(tempValue); - - if (addDynamic && Combo && Dynamic && _selectedItems.Exists(si => EqualityComparer<TValue>.Default.Equals(GetValue(si), GetValue(item))) is false) - { - _selectedItems.Add(item); - } - - await OnSelectItem.InvokeAsync(item); - } - else - { - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; - - var oldSelectedItem = _selectedItems.FirstOrDefault(); - - var isSameItemSelected = oldSelectedItem == item; - - CurrentValue = GetValue(item); - - if (addDynamic && Combo && Dynamic) - { - if (_selectedItems.Any()) - { - _selectedItems.Clear(); - } - - _selectedItems.Add(item); - } - - await CloseCallout(); - - await ClearSearchBox(); - - await ClearComboBoxInput(); - - if (isSameItemSelected && IsReselectable is false) return; - - await OnSelectItem.InvokeAsync(item); - } - - await OnValuesChange.InvokeAsync([.. _selectedItems]); - } - internal string GetItemWrapperCssClasses(TItem item) { var stringBuilder = new StringBuilder("bit-drp-iwr"); @@ -532,7 +471,7 @@ internal bool GetIsSelected(TItem item) if (value is null) return false; - if (IsMultiSelect) + if (MultiSelect) { return Values?.Contains(value) ?? false; } @@ -544,585 +483,667 @@ internal bool GetIsSelected(TItem item) - protected override string RootElementClass => "bit-drp"; - - protected override void RegisterCssClasses() + internal string? GetAriaLabel(TItem item) { - ClassBuilder.Register(() => Classes?.Root); - - ClassBuilder.Register(() => Required ? "bit-drp-req" : string.Empty); - - ClassBuilder.Register(() => _selectedItems?.Count > 0 ? "bit-drp-hvl" : string.Empty); - - ClassBuilder.Register(() => Chips ? "bit-drp-sch" : string.Empty); + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.AriaLabel; + } - ClassBuilder.Register(() => NoBorder ? "bit-drp-nbd" : string.Empty); + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.AriaLabel; + } - ClassBuilder.Register(() => Transparent ? "bit-drp-trn" : string.Empty); - } + if (NameSelectors is null) return null; - protected override void RegisterCssStyles() - { - StyleBuilder.Register(() => Styles?.Root); + if (NameSelectors.AriaLabel.Selector is not null) + { + return NameSelectors.AriaLabel.Selector!(item); + } - StyleBuilder.Register(() => FitWidth ? "width:fit-content" : string.Empty); + return item.GetValueFromProperty<string?>(NameSelectors.AriaLabel.Name); } - protected override async Task OnInitializedAsync() + internal string? GetClass(TItem item) { - _dropdownId = $"Dropdown-{UniqueId}"; - _calloutId = $"{_dropdownId}-callout"; - _scrollContainerId = $"{_dropdownId}-scroll-container"; - _headerId = $"{_dropdownId}-header"; - _footerId = $"{_dropdownId}-footer"; - - _labelId = $"{_dropdownId}-label"; - _dropdownTextContainerId = $"{_dropdownId}-text-container"; + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.Class; + } - if (ItemsProvider is null && Items is null) + if (item is BitDropdownOption<TValue> dropdownOption) { - Items = []; + return dropdownOption.Class; } - _selectedItems ??= []; + if (NameSelectors is null) return null; - OnValueChanged += HandleOnValueChanged; + if (NameSelectors.Class.Selector is not null) + { + return NameSelectors.Class.Selector!(item); + } - _dotnetObj = DotNetObjectReference.Create(this); + return item.GetValueFromProperty<string?>(NameSelectors.Class.Name); + } - if (IsMultiSelect) + internal string? GetId(TItem item) + { + if (item is BitDropdownItem<TValue> dropdownItem) { - if (ValuesHasBeenSet is false && DefaultValues is not null) - { - await AssignValues(DefaultValues); - } + return dropdownItem.Id; } - else + + if (item is BitDropdownOption<TValue> dropdownOption) { - if (ValueHasBeenSet is false && DefaultValue is not null) - { - Value = DefaultValue; - } + return dropdownOption.Id; } - UpdateSelectedItemsFromValues(); + if (NameSelectors is null) return null; - await base.OnInitializedAsync(); - } + if (NameSelectors.Id.Selector is not null) + { + return NameSelectors.Id.Selector!(item); + } - protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out TValue result, [NotNullWhen(false)] out string? parsingErrorMessage) - => throw new NotSupportedException($"This component does not parse string inputs. Bind to the '{nameof(CurrentValue)}' property, not '{nameof(CurrentValueAsString)}'."); + return item.GetValueFromProperty<string?>(NameSelectors.Id.Name); + } - protected override void CreateFieldIdentifier() + internal object? GetData(TItem item) { - if (IsMultiSelect) + if (item is BitDropdownItem<TValue> dropdownItem) { - CreateFieldIdentifier(ValuesExpression, typeof(ICollection<TValue?>)); + return dropdownItem.Data; } - else + + if (item is BitDropdownOption<TValue> dropdownOption) { - base.CreateFieldIdentifier(); + return dropdownOption.Data; } - } + if (NameSelectors is null) return null; + if (NameSelectors.Data.Selector is not null) + { + return NameSelectors.Data.Selector!(item); + } - private void UpdateSelectedItemsFromValues() - { - var items = ItemsProvider is not null ? _lastShowItems : Items; - if (items is null) return; + return item.GetValueFromProperty<object?>(NameSelectors.Data.Name); + } - if (ItemsProvider is null) + internal bool GetIsEnabled(TItem item) + { + if (item is BitDropdownItem<TValue> dropdownItem) { - _selectedItems.Clear(); + return dropdownItem.IsEnabled; } - var comparer = EqualityComparer<TValue>.Default; - if (IsMultiSelect) + if (item is BitDropdownOption<TValue> dropdownOption) { - if (Values is not null) - { - foreach (var item in items) - { - if (GetItemType(item) != BitDropdownItemType.Normal) continue; - if (Values.Any(v => comparer.Equals(v, GetValue(item))) is false) continue; - - _selectedItems.Add(item); - } - } + return dropdownOption.IsEnabled; } - else - { - var item = items.FirstOrDefault(i => comparer.Equals(GetValue(i), CurrentValue) && GetItemType(i) == BitDropdownItemType.Normal); - if (item is not null) - { - if (_selectedItems.Any()) - { - _selectedItems.Clear(); - } + if (NameSelectors is null) return true; - _selectedItems.Add(item); - } + if (NameSelectors.IsEnabled.Selector is not null) + { + return NameSelectors.IsEnabled.Selector!(item); } - ClassBuilder.Reset(); + return item.GetValueFromProperty(NameSelectors.IsEnabled.Name, true); } - private async Task CloseCallout() + internal bool GetIsHidden(TItem item) { - if (IsEnabled is false) return; + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.IsHidden; + } - if (IsOpen is false) return; + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.IsHidden; + } - if (await AssignIsOpen(false) is false) return; + if (NameSelectors is null) return false; - await ToggleCallout(); + if (NameSelectors.IsHidden.Selector is not null) + { + return NameSelectors.IsHidden.Selector!(item); + } - StateHasChanged(); + return item.GetValueFromProperty<bool>(NameSelectors.IsHidden.Name); } - private async Task HandleOnClick(MouseEventArgs e) + internal BitDropdownItemType GetItemType(TItem item) { - if (IsEnabled is false) return; - - if (await AssignIsOpen(true) is false) return; - - await ToggleCallout(); - - await OnClick.InvokeAsync(e); - await FocusOnComboBoxInput(); - await FocusOnSearchBox(); - } - - private void HandleOnValueChanged(object? sender, EventArgs args) => UpdateSelectedItemsFromValues(); - - private void HandleSearchBoxFocusIn() => _inputSearchHasFocus = true; - - private void HandleSearchBoxFocusOut() => _inputSearchHasFocus = false; + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.ItemType; + } - private Task HandleSearchBoxOnClear() => ClearSearchBox(); + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.ItemType; + } - private async Task HandleFilterChange(ChangeEventArgs e) - { - if (IsEnabled is false) return; - if (ShowSearchBox is false) return; + if (NameSelectors is null) return BitDropdownItemType.Normal; - _searchText = e.Value?.ToString(); + if (NameSelectors.ItemType.Selector is not null) + { + return NameSelectors.ItemType.Selector!(item); + } - await OnSearch.InvokeAsync(_searchText); - await SearchVirtualized(); + return item.GetValueFromProperty<BitDropdownItemType>(NameSelectors.ItemType.Name); } - private async Task ClearSearchBox() + internal string? GetStyle(TItem item) { - if (IsEnabled is false) return; - if (ShowSearchBox is false) return; - if (_searchText.HasNoValue()) return; + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.Style; + } - _searchText = null; + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.Style; + } - await OnSearch.InvokeAsync(_searchText); - await SearchVirtualized(); - } + if (NameSelectors is null) return null; - private async ValueTask FocusOnSearchBox() - { - if (IsEnabled is false) return; - if (ShowSearchBox is false) return; - if (AutoFocusSearchBox is false) return; - if (IsOpen is false) return; + if (NameSelectors.Style.Selector is not null) + { + return NameSelectors.Style.Selector!(item); + } - await _searchInputRef.FocusAsync(); + return item.GetValueFromProperty<string?>(NameSelectors.Style.Name); } - private async Task ClearComboBoxInput() + internal string? GetText(TItem? item) { - if (IsEnabled is false) return; - if (Combo is false) return; - if (_searchText.HasNoValue()) return; + if (item is null) return null; - _searchText = null; - } + if (item is BitDropdownItem<TValue> dropdownItem) + { + return dropdownItem.Text; + } - private async ValueTask FocusOnComboBoxInput() - { - if (IsEnabled is false) return; - if (IsOpen is false) return; - if (Combo is false) return; - if (_isResponsiveMode) return; + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.Text; + } - await _comboBoxInputRef.FocusAsync(); - } + if (NameSelectors is null) return null; - private ICollection<TItem> GetSearchedItems() - { - if (Items is null) return []; + if (NameSelectors.Text.Selector is not null) + { + return NameSelectors.Text.Selector!(item); + } - return _searchText.HasNoValue() - ? Items - : SearchFunction is not null - ? SearchFunction.Invoke(Items, _searchText!) - : Items.Where(i => GetItemType(i) == BitDropdownItemType.Normal - && (GetText(i)?.Contains(_searchText!, StringComparison.OrdinalIgnoreCase) ?? false)) - .ToArray(); + return item.GetValueFromProperty<string?>(NameSelectors.Text.Name); } - private string GetSearchBoxClasses() + internal string? GetTitle(TItem item) { - var className = new StringBuilder("bit-drp-sb"); - - if (_searchText.HasValue()) + if (item is BitDropdownItem<TValue> dropdownItem) { - className.Append(" bit-drp-shv"); + return dropdownItem.Title; } - if (_inputSearchHasFocus) + if (item is BitDropdownOption<TValue> dropdownOption) { - className.Append(" bit-drp-shf"); + return dropdownOption.Title; } - return className.ToString(); - } - - private string GetDropdownAriaLabelledby => Label.HasValue() ? $"{_labelId} {_dropdownTextContainerId}" : _dropdownTextContainerId; + if (NameSelectors is null) return null; - private async Task SearchVirtualized() - { - if (ItemsProvider is null) return; - if (_virtualizeElement is null) return; + if (NameSelectors.Title.Selector is not null) + { + return NameSelectors.Title.Selector!(item); + } - await _virtualizeElement.RefreshDataAsync(); + return item.GetValueFromProperty<string?>(NameSelectors.Title.Name); } - private async Task HandleOnClearClick() + internal TValue? GetValue(TItem? item) { - if (IsEnabled is false) return; + if (item is null) return default; - if (IsMultiSelect) + if (item is BitDropdownItem<TValue> dropdownItem) { - if (ValuesHasBeenSet && ValuesChanged.HasDelegate is false) return; - - await AssignValues(Array.Empty<TValue?>()); - await OnValuesChange.InvokeAsync(); + return dropdownItem.Value; } - else - { - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; - CurrentValue = default; + if (item is BitDropdownOption<TValue> dropdownOption) + { + return dropdownOption.Value; } - UpdateSelectedItemsFromValues(); + if (NameSelectors is null) return default; + + if (NameSelectors.Value.Selector is not null) + { + return NameSelectors.Value.Selector!(item); + } + return item.GetValueFromProperty<TValue?>(NameSelectors.Value.Name); } - private async Task HandleOnAddItemComboClick() + + + protected override string RootElementClass => "bit-drp"; + + protected override void RegisterCssClasses() { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + ClassBuilder.Register(() => Classes?.Root); - await AddDynamicItem(); + ClassBuilder.Register(() => Required ? "bit-drp-req" : string.Empty); - _searchText = string.Empty; + ClassBuilder.Register(() => _selectedItems?.Count > 0 ? "bit-drp-hvl" : string.Empty); - if (_isResponsiveMode && IsMultiSelect) - { - await _comboBoxInputResponsiveRef.FocusAsync(); + ClassBuilder.Register(() => Chips ? "bit-drp-sch" : string.Empty); - return; - } + ClassBuilder.Register(() => NoBorder ? "bit-drp-nbd" : string.Empty); - await CloseCallout(); + ClassBuilder.Register(() => Transparent ? "bit-drp-trn" : string.Empty); } - private async Task ToggleCallout() + protected override void RegisterCssStyles() { - if (IsEnabled is false) return; + StyleBuilder.Register(() => Styles?.Root); - _isResponsiveMode = await _js.ToggleCallout(_dotnetObj, - _dropdownId, - null, - _calloutId, - null, - IsOpen, - IsResponsive ? BitResponsiveMode.Panel : BitResponsiveMode.None, - DropDirection, - Dir is BitDir.Rtl, - _scrollContainerId, - ShowSearchBox && Combo is false ? 32 : 0, - CalloutHeaderTemplate is not null ? _headerId : "", - CalloutFooterTemplate is not null ? _footerId : "", - PreserveCalloutWidth is false, - RootElementClass); + StyleBuilder.Register(() => FitWidth ? "width:fit-content" : string.Empty); } - private async ValueTask<ItemsProviderResult<TItem>> InternalItemsProvider(ItemsProviderRequest request) + protected override async Task OnInitializedAsync() { - if (ItemsProvider is null) return default; - - // Debounce the requests. This eliminates a lot of redundant queries at the cost of slight lag after interactions. - // TODO: Consider making this configurable, or smarter (e.g., doesn't delay on first call in a batch, then the amount - // of delay increases if you rapidly issue repeated requests, such as when scrolling a long way) - await Task.Delay(100); + _dropdownId = $"Dropdown-{UniqueId}"; + _calloutId = $"{_dropdownId}-callout"; + _scrollContainerId = $"{_dropdownId}-scroll-container"; + _headerId = $"{_dropdownId}-header"; + _footerId = $"{_dropdownId}-footer"; - if (request.CancellationToken.IsCancellationRequested) return default; + _labelId = $"{_dropdownId}-label"; + _dropdownTextContainerId = $"{_dropdownId}-text-container"; - // Combine the query parameters from Virtualize with the ones from PaginationState - var providerRequest = new BitDropdownItemsProviderRequest<TItem>(request.StartIndex, request.Count, _searchText, request.CancellationToken); - var providerResult = await ItemsProvider(providerRequest); + if (ItemsProvider is null && Items is null) + { + Items = []; + } - if (request.CancellationToken.IsCancellationRequested) return default; + _selectedItems ??= []; - _lastShowItems = [.. providerResult.Items]; + OnValueChanged += HandleOnValueChanged; - return new ItemsProviderResult<TItem>(providerResult.Items, providerResult.TotalItemCount); - } + _dotnetObj = DotNetObjectReference.Create(this); - internal string? GetAriaLabel(TItem item) - { - if (item is BitDropdownItem<TValue> dropdownItem) + if (MultiSelect) { - return dropdownItem.AriaLabel; + if (ValuesHasBeenSet is false && DefaultValues is not null) + { + await AssignValues(DefaultValues); + } } - - if (item is BitDropdownOption<TValue> dropdownOption) + else { - return dropdownOption.AriaLabel; + if (ValueHasBeenSet is false && DefaultValue is not null) + { + Value = DefaultValue; + } } - if (NameSelectors is null) return null; - - if (NameSelectors.AriaLabel.Selector is not null) - { - return NameSelectors.AriaLabel.Selector!(item); - } + UpdateSelectedItemsFromValues(); - return item.GetValueFromProperty<string?>(NameSelectors.AriaLabel.Name); + await base.OnInitializedAsync(); } - internal string? GetClass(TItem item) + protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out TValue result, [NotNullWhen(false)] out string? parsingErrorMessage) + => throw new NotSupportedException($"This component does not parse string inputs. Bind to the '{nameof(CurrentValue)}' property, not '{nameof(CurrentValueAsString)}'."); + + protected override void CreateFieldIdentifier() { - if (item is BitDropdownItem<TValue> dropdownItem) + if (MultiSelect) { - return dropdownItem.Class; + CreateFieldIdentifier(ValuesExpression, typeof(ICollection<TValue?>)); } - - if (item is BitDropdownOption<TValue> dropdownOption) + else { - return dropdownOption.Class; + base.CreateFieldIdentifier(); } + } - if (NameSelectors is null) return null; - if (NameSelectors.Class.Selector is not null) + + private async Task AddOrRemoveSelectedItem(TItem? item, bool addDynamic = false) + { + if (item is null) return; + + if (MultiSelect) { - return NameSelectors.Class.Selector!(item); + if (ValuesHasBeenSet && ValuesChanged.HasDelegate is false) return; + + var isSelected = GetIsSelected(item) is false; + + var tempValue = Values?.ToList() ?? []; + + if (isSelected) + { + tempValue.Add(GetValue(item)); + } + else + { + tempValue.Remove(GetValue(item)); + } + + await AssignValues(tempValue); + + if (Combo) + { + if (addDynamic && Dynamic && _selectedItems.Exists(si => EqualityComparer<TValue>.Default.Equals(GetValue(si), GetValue(item))) is false) + { + _selectedItems.Add(item); + ClassBuilder.Reset(); + } + else if (addDynamic is false && isSelected is false && _selectedItems.Exists(si => EqualityComparer<TValue>.Default.Equals(GetValue(si), GetValue(item)))) + { + _selectedItems.Remove(item); + ClassBuilder.Reset(); + } + } + + await OnSelectItem.InvokeAsync(item); } + else + { + if (InvalidValueBinding()) return; - return item.GetValueFromProperty<string?>(NameSelectors.Class.Name); + var oldSelectedItem = _selectedItems.FirstOrDefault(); + + var isSameItemSelected = oldSelectedItem == item; + + CurrentValue = GetValue(item); + + if (addDynamic && Combo && Dynamic) + { + if (_selectedItems.Any()) + { + _selectedItems.Clear(); + } + + _selectedItems.Add(item); + + ClassBuilder.Reset(); + } + + await CloseCallout(); + + await ClearSearchBox(); + + await ClearComboBoxInput(); + + if (isSameItemSelected && Reselectable is false) return; + + await OnSelectItem.InvokeAsync(item); + } + + SetIsSelectedForSelectedItems(); + await OnValuesChange.InvokeAsync([.. Values ?? []]); } - internal string? GetId(TItem item) + private void UpdateSelectedItemsFromValues() { - if (item is BitDropdownItem<TValue> dropdownItem) + var items = ItemsProvider is not null ? _lastShowItems : Items; + if (items is null) return; + + if (ItemsProvider is null) { - return dropdownItem.Id; + _selectedItems.Clear(); } - if (item is BitDropdownOption<TValue> dropdownOption) + var comparer = EqualityComparer<TValue>.Default; + if (MultiSelect) { - return dropdownOption.Id; + if (Values?.Any() ?? false) + { + foreach (var item in items) + { + if (GetItemType(item) != BitDropdownItemType.Normal) continue; + if (Values.Any(v => comparer.Equals(v, GetValue(item))) is false) continue; + if (ItemsProvider is not null && _selectedItems.Exists(si => EqualityComparer<TValue>.Default.Equals(GetValue(si), GetValue(item)))) continue; + + _selectedItems.Add(item); + } + } + else + { + _selectedItems.Clear(); + } } + else + { + var item = items.FirstOrDefault(i => comparer.Equals(GetValue(i), CurrentValue) && GetItemType(i) == BitDropdownItemType.Normal); - if (NameSelectors is null) return null; + if (item is not null) + { + if (_selectedItems.Any()) + { + _selectedItems.Clear(); + } - if (NameSelectors.Id.Selector is not null) - { - return NameSelectors.Id.Selector!(item); + _selectedItems.Add(item); + } } - return item.GetValueFromProperty<string?>(NameSelectors.Id.Name); + ClassBuilder.Reset(); + SetIsSelectedForSelectedItems(); } - internal object? GetData(TItem item) + private async Task CloseCallout() { - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.Data; - } + if (IsEnabled is false) return; - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.Data; - } + if (IsOpen is false) return; + + if (await AssignIsOpen(false) is false) return; + + await ToggleCallout(); + + StateHasChanged(); + } - if (NameSelectors is null) return null; + private async Task HandleOnClick(MouseEventArgs e) + { + if (IsEnabled is false) return; - if (NameSelectors.Data.Selector is not null) - { - return NameSelectors.Data.Selector!(item); - } + if (await AssignIsOpen(true) is false) return; - return item.GetValueFromProperty<object?>(NameSelectors.Data.Name); + await ToggleCallout(); + + await OnClick.InvokeAsync(e); + await FocusOnComboBoxInput(); + await FocusOnSearchBox(); } - internal bool GetIsEnabled(TItem item) - { - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.IsEnabled; - } + private void HandleOnValueChanged(object? sender, EventArgs args) => UpdateSelectedItemsFromValues(); - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.IsEnabled; - } + private void HandleSearchBoxFocusIn() => _inputSearchHasFocus = true; - if (NameSelectors is null) return true; + private void HandleSearchBoxFocusOut() => _inputSearchHasFocus = false; - if (NameSelectors.IsEnabled.Selector is not null) - { - return NameSelectors.IsEnabled.Selector!(item); - } + private Task HandleSearchBoxOnClear() => ClearSearchBox(); - return item.GetValueFromProperty(NameSelectors.IsEnabled.Name, true); + private async Task HandleFilterChange(ChangeEventArgs e) + { + if (IsEnabled is false) return; + if (ShowSearchBox is false) return; + + _searchText = e.Value?.ToString(); + + await OnSearch.InvokeAsync(_searchText); + await SearchVirtualized(); } - internal bool GetIsHidden(TItem item) + private async Task ClearSearchBox() { - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.IsHidden; - } + if (IsEnabled is false) return; + if (ShowSearchBox is false) return; + if (_searchText.HasNoValue()) return; - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.IsHidden; - } + _searchText = null; - if (NameSelectors is null) return false; + await OnSearch.InvokeAsync(_searchText); + await SearchVirtualized(); + } - if (NameSelectors.IsHidden.Selector is not null) - { - return NameSelectors.IsHidden.Selector!(item); - } + private async ValueTask FocusOnSearchBox() + { + if (IsEnabled is false) return; + if (ShowSearchBox is false) return; + if (AutoFocusSearchBox is false) return; + if (IsOpen is false) return; - return item.GetValueFromProperty<bool>(NameSelectors.IsHidden.Name); + await _searchInputRef.FocusAsync(); } - internal BitDropdownItemType GetItemType(TItem item) + private async Task ClearComboBoxInput() { - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.ItemType; - } + if (IsEnabled is false) return; + if (Combo is false) return; + if (_searchText.HasNoValue()) return; - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.ItemType; - } + _searchText = null; + } - if (NameSelectors is null) return BitDropdownItemType.Normal; + private async ValueTask FocusOnComboBoxInput() + { + if (IsEnabled is false) return; + if (IsOpen is false) return; + if (Combo is false) return; + if (_isResponsiveMode) return; - if (NameSelectors.ItemType.Selector is not null) - { - return NameSelectors.ItemType.Selector!(item); - } + await _comboBoxInputRef.FocusAsync(); + } - return item.GetValueFromProperty<BitDropdownItemType>(NameSelectors.ItemType.Name); + private ICollection<TItem> GetSearchedItems() + { + if (Items is null) return []; + + return _searchText.HasNoValue() + ? Items + : SearchFunction is not null + ? SearchFunction.Invoke(Items, _searchText!) + : Items.Where(i => GetItemType(i) == BitDropdownItemType.Normal + && (GetText(i)?.Contains(_searchText!, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToArray(); } - internal string? GetStyle(TItem item) + private string GetSearchBoxClasses() { - if (item is BitDropdownItem<TValue> dropdownItem) + var className = new StringBuilder("bit-drp-sb"); + + if (_searchText.HasValue()) { - return dropdownItem.Style; + className.Append(" bit-drp-shv"); } - if (item is BitDropdownOption<TValue> dropdownOption) + if (_inputSearchHasFocus) { - return dropdownOption.Style; + className.Append(" bit-drp-shf"); } - if (NameSelectors is null) return null; + return className.ToString(); + } - if (NameSelectors.Style.Selector is not null) - { - return NameSelectors.Style.Selector!(item); - } + private string GetDropdownAriaLabelledby => Label.HasValue() ? $"{_labelId} {_dropdownTextContainerId}" : _dropdownTextContainerId; - return item.GetValueFromProperty<string?>(NameSelectors.Style.Name); + private async Task SearchVirtualized() + { + if (ItemsProvider is null) return; + if (_virtualizeElement is null) return; + + await _virtualizeElement.RefreshDataAsync(); } - internal string? GetText(TItem? item) + private async Task HandleOnClearClick() { - if (item is null) return null; + if (IsEnabled is false) return; - if (item is BitDropdownItem<TValue> dropdownItem) + if (MultiSelect) { - return dropdownItem.Text; - } + if (ValuesHasBeenSet && ValuesChanged.HasDelegate is false) return; - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.Text; + await AssignValues([]); + await OnValuesChange.InvokeAsync(Values); } - - if (NameSelectors is null) return null; - - if (NameSelectors.Text.Selector is not null) + else { - return NameSelectors.Text.Selector!(item); + if (InvalidValueBinding()) return; + + CurrentValue = default; } - return item.GetValueFromProperty<string?>(NameSelectors.Text.Name); + UpdateSelectedItemsFromValues(); } - internal string? GetTitle(TItem item) + private async Task HandleOnAddItemComboClick() { - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.Title; - } + if (IsEnabled is false || InvalidValueBinding()) return; - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.Title; - } + await AddDynamicItem(); - if (NameSelectors is null) return null; + _searchText = string.Empty; - if (NameSelectors.Title.Selector is not null) + if (_isResponsiveMode && MultiSelect) { - return NameSelectors.Title.Selector!(item); + await _comboBoxInputResponsiveRef.FocusAsync(); + + return; } - return item.GetValueFromProperty<string?>(NameSelectors.Title.Name); + await CloseCallout(); } - internal TValue? GetValue(TItem? item) + private async Task ToggleCallout() { - if (item is null) return default; + if (IsEnabled is false) return; - if (item is BitDropdownItem<TValue> dropdownItem) - { - return dropdownItem.Value; - } + _isResponsiveMode = await _js.ToggleCallout(_dotnetObj, + _dropdownId, + null, + _calloutId, + null, + IsOpen, + Responsive ? BitResponsiveMode.Panel : BitResponsiveMode.None, + DropDirection, + Dir is BitDir.Rtl, + _scrollContainerId, + ShowSearchBox && Combo is false ? 32 : 0, + CalloutHeaderTemplate is not null ? _headerId : "", + CalloutFooterTemplate is not null ? _footerId : "", + PreserveCalloutWidth is false, + RootElementClass); + } - if (item is BitDropdownOption<TValue> dropdownOption) - { - return dropdownOption.Value; - } + private async ValueTask<ItemsProviderResult<TItem>> InternalItemsProvider(ItemsProviderRequest request) + { + if (ItemsProvider is null) return default; - if (NameSelectors is null) return default; + // Debounce the requests. This eliminates a lot of redundant queries at the cost of slight lag after interactions. + // TODO: Consider making this configurable, or smarter (e.g., doesn't delay on first call in a batch, then the amount + // of delay increases if you rapidly issue repeated requests, such as when scrolling a long way) + await Task.Delay(100); - if (NameSelectors.Value.Selector is not null) - { - return NameSelectors.Value.Selector!(item); - } + if (request.CancellationToken.IsCancellationRequested) return default; - return item.GetValueFromProperty<TValue?>(NameSelectors.Value.Name); + // Combine the query parameters from Virtualize with the ones from PaginationState + var providerRequest = new BitDropdownItemsProviderRequest<TItem>(request.StartIndex, request.Count, _searchText, request.CancellationToken); + var providerResult = await ItemsProvider(providerRequest); + + if (request.CancellationToken.IsCancellationRequested) return default; + + _lastShowItems = [.. providerResult.Items]; + + return new ItemsProviderResult<TItem>(providerResult.Items, providerResult.TotalItemCount); } private bool CheckInvalidForSelect(TItem item) @@ -1164,8 +1185,7 @@ NameSelectors.ItemType.Selector is not null && private async Task HandleOnKeyDown(KeyboardEventArgs eventArgs) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (eventArgs.Key == "Escape") { @@ -1181,7 +1201,7 @@ private async Task HandleOnKeyDown(KeyboardEventArgs eventArgs) _searchText = string.Empty; - if (_isResponsiveMode && IsMultiSelect) return; + if (_isResponsiveMode && MultiSelect) return; await CloseCallout(); } @@ -1195,8 +1215,7 @@ private async Task HandleOnKeyDown(KeyboardEventArgs eventArgs) private async Task HandleOnComboInput(ChangeEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; _searchText = e.Value?.ToString(); await SearchVirtualized(); @@ -1218,7 +1237,7 @@ private async Task RemoveLastSelectedItem() { if (_selectedItems.Any() is false) return; - if (IsMultiSelect) + if (MultiSelect) { var lastItem = _selectedItems.Last(); await AddOrRemoveSelectedItem(lastItem); @@ -1324,7 +1343,7 @@ private async Task AddDynamicItem() private string? GetText() { - return IsMultiSelect ? string.Join(MultiSelectDelimiter, _selectedItems.Select(GetText)) : GetText(_selectedItems.FirstOrDefault()); + return MultiSelect ? string.Join(MultiSelectDelimiter, _selectedItems.Select(GetText)) : GetText(_selectedItems.FirstOrDefault()); } private void OnSetValues() @@ -1334,6 +1353,38 @@ private void OnSetValues() EditContext?.NotifyFieldChanged(FieldIdentifier); } + private void SetIsSelected(TItem item, bool value) + { + if (item is BitDropdownItem<TValue> dropdownItem) + { + dropdownItem.IsSelected = value; + } + + if (item is BitDropdownItem<TValue> dropdownOption) + { + dropdownOption.IsSelected = value; + } + + if (NameSelectors is null) return; + + item.SetValueToProperty(NameSelectors.IsSelected, value); + } + + private void SetIsSelectedForSelectedItems() + { + if (Items is null) return; + + foreach (var it in Items) + { + SetIsSelected(it, false); + } + + foreach (var it in _selectedItems) + { + SetIsSelected(it, true); + } + } + public async ValueTask DisposeAsync() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownItem.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownItem.cs index f847e63ab4..606fe962e6 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownItem.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownItem.cs @@ -56,4 +56,11 @@ public class BitDropdownItem<TValue> /// The value of the dropdown item. /// </summary> public TValue? Value { get; set; } + + + + /// <summary> + /// Determines if the item is selected. This property's value is assigned by the component. + /// </summary> + public bool IsSelected { get; internal set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownNameSelectors.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownNameSelectors.cs index b1a2ce438a..c502c95e61 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownNameSelectors.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownNameSelectors.cs @@ -57,6 +57,8 @@ public class BitDropdownNameSelectors<TItem, TValue> /// </summary> public BitNameSelectorPair<TItem, TValue?> Value { get; set; } = new(nameof(BitDropdownItem<TValue>.Value)); + + /// <summary> /// The setter function for updating Text property of custom item in Dynamic ComboBox mode upon new item addition. /// </summary> @@ -66,4 +68,11 @@ public class BitDropdownNameSelectors<TItem, TValue> /// The setter function for updating Value property of custom item in Dynamic ComboBox mode upon new item addition. /// </summary> public Action<TItem, TValue>? ValueSetter { get; set; } + + + + /// <summary> + /// The IsSelected field name of the custom input class. This property's value is assigned by the component. + /// </summary> + public string IsSelected { get; set; } = nameof(BitDropdownItem<TValue>.IsSelected); } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs index 1aec1bb581..01e779fd1b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs @@ -63,6 +63,14 @@ public partial class BitDropdownOption<TValue> : ComponentBase, IDisposable [Parameter] public TValue? Value { get; set; } + + /// <summary> + /// Determines if the option is selected. This property's value is assigned by the component. + /// </summary> + public bool IsSelected { get; internal set; } + + + protected override async Task OnInitializedAsync() { Parent.RegisterOption(this); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/_BitDropdownItem.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/_BitDropdownItem.razor index 80f40ae913..36feec0168 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/_BitDropdownItem.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/_BitDropdownItem.razor @@ -29,7 +29,7 @@ } else if (itemType == BitDropdownItemType.Normal) { - @if (Dropdown.IsMultiSelect) + @if (Dropdown.MultiSelect) { <div style="@(isHidden ? "display: none" : "") @Dropdown.Styles?.ItemWrapper" class="@Dropdown.GetItemWrapperCssClasses(Item) @Dropdown.Classes?.ItemWrapper"> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor index 6ce0fd36c5..77b3d2e2c6 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor @@ -26,7 +26,7 @@ type="file" disabled="@(IsEnabled is false)" aria-labelledby="@(Label.HasValue() ? Label : null)" - multiple="@IsMultiSelect" + multiple="@MultiSelect" accept="@(Accept ?? string.Join(",", AllowedExtensions))" /> @if (Files is not null) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs index daed86ab61..b5f0335b08 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs @@ -35,22 +35,22 @@ public partial class BitFileUpload : BitComponentBase, IAsyncDisposable /// <summary> /// Filters files by extension. /// </summary> - [Parameter] public IReadOnlyCollection<string> AllowedExtensions { get; set; } = new List<string> { "*" }; + [Parameter] public IReadOnlyCollection<string> AllowedExtensions { get; set; } = ["*"]; /// <summary> /// Calculate the chunk size dynamically based on the user's Internet speed between 512 KB and 10 MB. /// </summary> - [Parameter] public bool AutoChunkSizeEnabled { get; set; } + [Parameter] public bool AutoChunkSize { get; set; } /// <summary> /// Automatically starts the upload file(s) process immediately after selecting the file(s). /// </summary> - [Parameter] public bool AutoUploadEnabled { get; set; } + [Parameter] public bool AutoUpload { get; set; } /// <summary> /// Enables or disables the chunked upload feature. /// </summary> - [Parameter] public bool ChunkedUploadEnabled { get; set; } = true; + [Parameter] public bool ChunkedUpload { get; set; } /// <summary> /// The size of each chunk of file upload in bytes. @@ -72,7 +72,7 @@ public partial class BitFileUpload : BitComponentBase, IAsyncDisposable /// <summary> /// Enables multi-file select and upload. /// </summary> - [Parameter] public bool IsMultiSelect { get; set; } + [Parameter] public bool MultiSelect { get; set; } /// <summary> /// The text of select file button. @@ -327,7 +327,7 @@ public async Task Browse() /// Receive upload progress notification from underlying javascript. /// </summary> [JSInvokable("HandleChunkUploadProgress")] - public async Task HandleChunkUploadProgress(int index, long loaded) + public async Task __HandleChunkUploadProgress(int index, long loaded) { if (Files is null) return; @@ -343,14 +343,14 @@ public async Task HandleChunkUploadProgress(int index, long loaded) /// Receive upload finished notification from underlying JavaScript. /// </summary> [JSInvokable("HandleChunkUpload")] - public async Task HandleChunkUpload(int fileIndex, int responseStatus, string responseText) + public async Task __HandleChunkUpload(int fileIndex, int responseStatus, string responseText) { if (Files is null || UploadStatus == BitFileUploadStatus.Paused) return; var file = Files[fileIndex]; if (file.Status != BitFileUploadStatus.InProgress) return; - file.TotalUploadedSize += ChunkedUploadEnabled ? _internalChunkSize : file.Size; + file.TotalUploadedSize += ChunkedUpload ? _internalChunkSize : file.Size; file.LastChunkUploadedSize = 0; UpdateChunkSize(fileIndex); @@ -423,7 +423,7 @@ private async Task HandleOnChange() await OnChange.InvokeAsync([.. Files]); - if (AutoUploadEnabled) + if (AutoUpload) { await Upload(); } @@ -462,7 +462,7 @@ private async Task UploadOneFile(BitFileInfo fileInfo, string? uploadUrl = null) long to; long from = 0; - if (ChunkedUploadEnabled) + if (ChunkedUpload) { from = fileInfo.TotalUploadedSize; if (fileInfo.Size > _internalChunkSize) @@ -502,7 +502,7 @@ private async Task PauseUploadOneFile(int index) private void UpdateChunkSize(int fileIndex) { - if (Files is null || AutoChunkSizeEnabled is false) return; + if (Files is null || AutoChunkSize is false) return; var dtNow = DateTime.UtcNow; var duration = (dtNow - Files[fileIndex].StartTimeUpload.GetValueOrDefault(dtNow)).TotalMilliseconds; @@ -667,7 +667,7 @@ private static string AddQueryString(string? url, IReadOnlyDictionary<string, st private void OnSetChunkSize() { - _internalChunkSize = ChunkSize.HasValue is false || AutoChunkSizeEnabled + _internalChunkSize = ChunkSize.HasValue is false || AutoChunkSize ? MIN_CHUNK_SIZE : ChunkSize.Value; } @@ -692,6 +692,12 @@ protected virtual async ValueTask DisposeAsync(bool disposing) await _dropZoneRef.DisposeAsync(); } catch (JSDisconnectedException) { } // we can ignore this exception here + catch (JSException ex) + { + // it seems it's safe to just ignore this exception here. + // otherwise it will blow up the MAUI app in a page refresh for example. + Console.WriteLine(ex.Message); + } } if (_dotnetObj is not null) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor index 7339de3fa1..efb55708aa 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor @@ -70,15 +70,17 @@ name="@Name" step="@_step" id="@_inputId" - autocomplete="off" readonly="@ReadOnly" required="@Required" style="@Styles?.Input" + autofocus="@AutoFocus" + inputmode="@GetInputMode()" placeholder="@Placeholder" aria-labelledby="@_labelId" value="@CurrentValueAsString" disabled="@(IsEnabled is false)" class="bit-nfl-inp @Classes?.Input" + autocomplete="@(AutoComplete ?? "off")" type="@(NumberFormat.HasValue() ? "text" : "number")" aria-valuemin="@_min" aria-valuemax="@_max" @@ -103,7 +105,7 @@ aria-disabled="@(IsEnabled is false)"> <span style="@Styles?.IncrementIconContainer" class="bit-nfl-aic @Classes?.IncrementIconContainer"> <i style="@Styles?.IncrementIcon" - class="bit-nfl-bic bit-icon bit-icon--@IncrementIconName @Classes?.IncrementIcon" /> + class="bit-nfl-bic bit-icon bit-icon--@(IncrementIconName ?? "ChevronDownSmall bit-ico-r180") @Classes?.IncrementIcon" /> </span> </button> @@ -121,7 +123,7 @@ aria-disabled="@(IsEnabled is false)"> <span style="@Styles?.DecrementIconContainer" class="bit-nfl-aic @Classes?.DecrementIconContainer"> <i style="@Styles?.DecrementIcon" - class="bit-nfl-bic bit-icon bit-icon--@DecrementIconName @Classes?.DecrementIcon" /> + class="bit-nfl-bic bit-icon bit-icon--@(DecrementIconName ?? "ChevronDownSmall") @Classes?.DecrementIcon" /> </span> </button> </span> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor.cs index 26c5fe1dba..f5c0574521 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.razor.cs @@ -79,7 +79,7 @@ public BitNumberField() /// <summary> /// Custom icon name for the decrement button. /// </summary> - [Parameter] public string DecrementIconName { get; set; } = "ChevronDownSmall"; + [Parameter] public string? DecrementIconName { get; set; } /// <summary> /// Initial value of the number field. @@ -104,7 +104,7 @@ public BitNumberField() /// <summary> /// Custom icon name for the increment button. /// </summary> - [Parameter] public string IncrementIconName { get; set; } = "ChevronUpSmall"; + [Parameter] public string? IncrementIconName { get; set; } /// <summary> /// The position of the label in regards to the number field. @@ -313,8 +313,7 @@ protected override void Dispose(bool disposing) private async Task HandleOnKeyDown(KeyboardEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; switch (e.Key) { @@ -381,8 +380,7 @@ private async Task HandleOnFocus(FocusEventArgs e) private async Task HandleOnPointerDown(bool isIncrement) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; //Change focus from input to number field if (isIncrement) @@ -587,4 +585,11 @@ private void OnSetStep() _step = (TValue)(object)1; } } + + private string GetInputMode() + { + return (_typeOfValue == typeof(decimal) || _typeOfValue == typeof(double) || _typeOfValue == typeof(float)) + ? "decimal" + : "numeric"; + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.razor.cs index cb595add54..921e04a15e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.razor.cs @@ -119,8 +119,7 @@ public partial class BitOtpInput : BitInputBase<string?>, IDisposable [JSInvokable] public async Task SetPastedData(string pastedValue) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (pastedValue.HasNoValue()) return; if (Type is BitInputType.Number && int.TryParse(pastedValue, out _) is false) return; @@ -297,7 +296,7 @@ private async Task HandleOnInput(ChangeEventArgs e, int index) _inputValues[index] = string.Empty; await Task.Delay(1); // waiting for input default behavior before setting a new value. - if (IsEnabled is false || (ValueHasBeenSet && ValueChanged.HasDelegate is false)) + if (IsEnabled is false || InvalidValueBinding()) { _inputValues[index] = oldValue; } @@ -336,8 +335,8 @@ private async Task HandleOnInput(ChangeEventArgs e, int index) private async Task HandleOnKeyDown(KeyboardEventArgs e, int index) { - if (IsEnabled is false || e.Code is null) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; + if (e.Code is null) return; await NavigateInput(e.Code, e.Key, index); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Rating/BitRating.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Rating/BitRating.razor.cs index cd6aff21d1..0286129cc5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Rating/BitRating.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Rating/BitRating.razor.cs @@ -136,8 +136,8 @@ private double GetPercentage(int index) private async Task HandleOnClick(int index) { - if (IsEnabled is false || ReadOnly) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; + if (ReadOnly) return; if (index > Max || index < (AllowZeroStars ? 0 : 1)) return; CurrentValue = index; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor index c416c39c5f..8c547fcce3 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor @@ -22,11 +22,13 @@ name="@Name" id="@_inputId" role="searchbox" + inputmode="@_inputMode" value="@CurrentValue" style="@Styles?.Input" + autofocus="@AutoFocus" aria-label="@AriaLabel" placeholder="@Placeholder" - autocomplete="@Autocomplete" + autocomplete="@AutoComplete" disabled="@(IsEnabled is false)" class="bit-srb-inp @Classes?.Input" /> @if (HideClearButton is false && CurrentValue.HasValue()) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs index 92d6696df9..ce55448a8f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs @@ -6,6 +6,7 @@ public partial class BitSearchBox : BitTextInputBase<string?>, IAsyncDisposable { private bool _isOpen; private bool _disposed; + private string? _inputMode; private bool _inputHasFocus; private int _selectedIndex = -1; private string _inputId = string.Empty; @@ -21,11 +22,6 @@ public partial class BitSearchBox : BitTextInputBase<string?>, IAsyncDisposable - /// <summary> - /// Specifies the value of the autocomplete attribute of the input component. - /// </summary> - [Parameter] public string? Autocomplete { get; set; } - /// <summary> /// Custom CSS classes for different parts of the BitSearchBox. /// </summary> @@ -64,6 +60,13 @@ public partial class BitSearchBox : BitTextInputBase<string?>, IAsyncDisposable /// </summary> [Parameter] public string IconName { get; set; } = "Search"; + /// <summary> + /// Sets the inputmode html attribute of the input element. + /// </summary> + [Parameter] + [CallOnSet(nameof(SetInputMode))] + public BitInputMode? InputMode { get; set; } + /// <summary> /// The maximum number of items or suggestions that will be displayed. /// </summary> @@ -201,6 +204,10 @@ protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(fa } + private void SetInputMode() + { + _inputMode = InputMode?.ToString().ToLower(); + } private void HandleOnValueChanged(object? sender, EventArgs args) { @@ -245,8 +252,7 @@ private async Task HandleOnClearButtonClick() private async Task HandleOnKeyDown(KeyboardEventArgs eventArgs) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (eventArgs.Key == "Escape") { @@ -384,8 +390,7 @@ private async Task ChangeSelectedItem(bool isArrowUp) private async Task HandleOnItemClick(string item) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; CurrentValue = item; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor index 0d73410147..68f92e7ce3 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor @@ -108,7 +108,7 @@ aria-label="@IncrementAriaLabel"> <span style="@Styles?.IncrementIconWrapper" class="bit-spb-iwp @Classes?.IncrementIconWrapper"> <i style="@Styles?.IncrementIcon" - class="bit-spb-ico bit-icon bit-icon--@IncrementIconName @Classes?.IncrementIcon" /> + class="bit-spb-ico bit-icon bit-icon--@(IncrementIconName ?? "ChevronDownSmall bit-ico-r180") @Classes?.IncrementIcon" /> </span> </button> @@ -126,7 +126,7 @@ aria-label="@DecrementAriaLabel"> <span style="@Styles?.DecrementIconWrapper" class="bit-spb-iwp @Classes?.DecrementIconWrapper"> <i style="@Styles?.DecrementIcon" - class="bit-spb-ico bit-icon bit-icon--@DecrementIconName @Classes?.DecrementIcon" /> + class="bit-spb-ico bit-icon bit-icon--@(DecrementIconName ?? "ChevronDownSmall") @Classes?.DecrementIcon" /> </span> </button> </span> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor.cs index 08cadefafb..3835a3b806 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SpinButton/BitSpinButton.razor.cs @@ -59,7 +59,7 @@ public partial class BitSpinButton : BitInputBase<double> /// <summary> /// Custom icon name for the decrement button. /// </summary> - [Parameter] public string DecrementIconName { get; set; } = "ChevronDownSmall"; + [Parameter] public string? DecrementIconName { get; set; } /// <summary> /// The title to show when the mouse is placed on the decrement button. @@ -89,7 +89,7 @@ public partial class BitSpinButton : BitInputBase<double> /// <summary> /// Custom icon name for the increment button. /// </summary> - [Parameter] public string IncrementIconName { get; set; } = "ChevronUpSmall"; + [Parameter] public string? IncrementIconName { get; set; } /// <summary> /// The title to show when the mouse is placed on the increment button. @@ -297,8 +297,7 @@ private async Task ApplyValueChange(bool isIncrement) private async Task HandleOnPointerDown(bool isIncrement) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; //Change focus from input to number field if (isIncrement) @@ -364,18 +363,16 @@ private void ResetCts() private void HandleOnChange(ChangeEventArgs e) { - if (IsEnabled is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (IsInputReadOnly) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; _intermediateValue = GetCleanValue(e.Value?.ToString()); } private async Task HandleOnKeyDown(KeyboardEventArgs e) { - if (IsEnabled is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (IsInputReadOnly) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; switch (e.Key) { @@ -470,7 +467,7 @@ private void SetValue(double value) private async Task CheckIntermediateValueAndSetValue() { - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (InvalidValueBinding()) return; if (_intermediateValue == CurrentValueAsString) return; var isNumber = double.TryParse(_intermediateValue, out var numericValue); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor index 01f4d121f9..ebf5085642 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor @@ -37,7 +37,7 @@ </div> } - @if (IsMultiline && Type == BitInputType.Text) + @if (Multiline && Type is null or BitInputType.Text) { <textarea @ref="InputElement" @attributes="InputHtmlAttributes" @onclick="@HandleOnClick" @@ -48,14 +48,16 @@ @onfocusout="@HandleOnFocusOut" @onchange="@HandleOnStringValueChangeAsync" @oninput="@HandleOnStringValueInputAsync" - rows="@Rows" name="@Name" id="@_inputId" + rows="@(Rows ?? 3)" + tabindex="@TabIndex" readonly="@ReadOnly" required="@Required" style="@Styles?.Input" autofocus="@AutoFocus" maxlength="@MaxLength" + inputmode="@_inputMode" placeholder="@Placeholder" value="@CurrentValueAsString" disabled=@(IsEnabled is false) @@ -80,9 +82,11 @@ type="@_inputType" readonly="@ReadOnly" required="@Required" + tabindex="@TabIndex" style="@Styles?.Input" autofocus="@AutoFocus" maxlength="@MaxLength" + inputmode="@_inputMode" placeholder="@Placeholder" autocomplete="@AutoComplete" value="@CurrentValueAsString" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs index 4de247201c..6360913b41 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs @@ -5,8 +5,9 @@ namespace Bit.BlazorUI; public partial class BitTextField : BitTextInputBase<string?> { private bool _hasFocus; + private string? _inputMode; private bool _isPasswordRevealed; - private BitInputType _elementType; + private BitInputType? _elementType; private string _inputId = string.Empty; private string _labelId = string.Empty; private string _inputType = string.Empty; @@ -14,16 +15,6 @@ public partial class BitTextField : BitTextInputBase<string?> - /// <summary> - /// AutoComplete is a string that maps to the autocomplete attribute of the HTML input element. - /// </summary> - [Parameter] public string? AutoComplete { get; set; } - - /// <summary> - /// Determines if the text field is auto focused on first render. - /// </summary> - [Parameter] public bool AutoFocus { get; set; } - /// <summary> /// Whether to show the reveal password button for input type 'password'. /// </summary> @@ -50,53 +41,53 @@ public partial class BitTextField : BitTextInputBase<string?> [Parameter] public RenderFragment? DescriptionTemplate { get; set; } /// <summary> - /// Whether or not the text field is borderless. + /// The icon name for the icon shown in the far right end of the text field. /// </summary> - [Parameter, ResetClassBuilder] - public bool HasBorder { get; set; } = true; + [Parameter] public string? IconName { get; set; } /// <summary> - /// Whether or not the text field is a Multiline text field. + /// Sets the inputmode html attribute of the input element. /// </summary> - [Parameter, ResetClassBuilder] - public bool IsMultiline { get; set; } + [Parameter] + [CallOnSet(nameof(SetInputMode))] + public BitInputMode? InputMode { get; set; } /// <summary> - /// Whether or not the text field is underlined. + /// Label displayed above the text field and read by screen readers. /// </summary> - [Parameter, ResetClassBuilder] - public bool IsUnderlined { get; set; } + [Parameter] public string? Label { get; set; } /// <summary> - /// For multiline text fields, whether or not the field is resizable. + /// Shows the custom label for text field. /// </summary> - [Parameter, ResetClassBuilder] - public bool IsResizable { get; set; } = true; + [Parameter] public RenderFragment? LabelTemplate { get; set; } /// <summary> - /// The icon name for the icon shown in the far right end of the text field. + /// Specifies the maximum number of characters allowed in the input. /// </summary> - [Parameter] public string? IconName { get; set; } + [Parameter] public int MaxLength { get; set; } = -1; /// <summary> - /// Specifies whether to remove any leading or trailing whitespace from the value. + /// Whether or not the text field is a Multiline text field. /// </summary> - [Parameter] public bool IsTrimmed { get; set; } + [Parameter, ResetClassBuilder] + public bool Multiline { get; set; } /// <summary> - /// Label displayed above the text field and read by screen readers. + /// Removes the border of the text input. /// </summary> - [Parameter] public string? Label { get; set; } + [Parameter, ResetClassBuilder] + public bool NoBorder { get; set; } /// <summary> - /// Shows the custom label for text field. + /// Callback for when the input clicked. /// </summary> - [Parameter] public RenderFragment? LabelTemplate { get; set; } + [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; } /// <summary> - /// Specifies the maximum number of characters allowed in the input. + /// Callback for when focus moves into the input /// </summary> - [Parameter] public int MaxLength { get; set; } = -1; + [Parameter] public EventCallback<FocusEventArgs> OnFocus { get; set; } /// <summary> /// Callback for when focus moves into the input @@ -108,11 +99,6 @@ public partial class BitTextField : BitTextInputBase<string?> /// </summary> [Parameter] public EventCallback<FocusEventArgs> OnFocusOut { get; set; } - /// <summary> - /// Callback for when focus moves into the input - /// </summary> - [Parameter] public EventCallback<FocusEventArgs> OnFocus { get; set; } - /// <summary> /// Callback for when a keyboard key is pressed /// </summary> @@ -123,11 +109,6 @@ public partial class BitTextField : BitTextInputBase<string?> /// </summary> [Parameter] public EventCallback<KeyboardEventArgs> OnKeyUp { get; set; } - /// <summary> - /// Callback for when the input clicked. - /// </summary> - [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; } - /// <summary> /// Input placeholder text. /// </summary> @@ -145,15 +126,21 @@ public partial class BitTextField : BitTextInputBase<string?> [Parameter] public RenderFragment? PrefixTemplate { get; set; } /// <summary> - /// For multiline text, Number of rows. + /// For multiline text fields, whether or not the field is resizable. /// </summary> - [Parameter] public int Rows { get; set; } = 3; + [Parameter, ResetClassBuilder] + public bool Resizable { get; set; } /// <summary> /// Aria label for the reveal password button. /// </summary> [Parameter] public string? RevealPasswordAriaLabel { get; set; } + /// <summary> + /// For multiline text, Number of rows. + /// </summary> + [Parameter] public int? Rows { get; set; } + /// <summary> /// Custom CSS styles for different parts of the BitTextField. /// </summary> @@ -170,12 +157,28 @@ public partial class BitTextField : BitTextInputBase<string?> /// </summary> [Parameter] public RenderFragment? SuffixTemplate { get; set; } + /// <summary> + /// The value of the tabindex html attribute of the input element. + /// </summary> + [Parameter] public string? TabIndex { get; set; } + + /// <summary> + /// Specifies whether to remove any leading or trailing whitespace from the value. + /// </summary> + [Parameter] public bool Trim { get; set; } + /// <summary> /// Input type. /// </summary> [Parameter, ResetClassBuilder] [CallOnSet(nameof(SetElementType))] - public BitInputType Type { get; set; } = BitInputType.Text; + public BitInputType? Type { get; set; } + + /// <summary> + /// Whether or not the text field is underlined. + /// </summary> + [Parameter, ResetClassBuilder] + public bool Underlined { get; set; } @@ -193,15 +196,15 @@ protected override void RegisterCssClasses() { ClassBuilder.Register(() => Classes?.Root); - ClassBuilder.Register(() => IsMultiline && Type == BitInputType.Text - ? $"bit-tfl-{(IsResizable ? "mln" : "mlf")}" + ClassBuilder.Register(() => Multiline && Type is null or BitInputType.Text + ? $"bit-tfl-{(Resizable ? "mln" : "mlf")}" : string.Empty); ClassBuilder.Register(() => IsEnabled && Required ? "bit-tfl-req" : string.Empty); - ClassBuilder.Register(() => IsUnderlined ? "bit-tfl-und" : string.Empty); + ClassBuilder.Register(() => Underlined ? "bit-tfl-und" : string.Empty); - ClassBuilder.Register(() => HasBorder is false ? "bit-tfl-nbd" : string.Empty); + ClassBuilder.Register(() => NoBorder ? "bit-tfl-nbd" : string.Empty); ClassBuilder.Register(() => _hasFocus ? $"bit-tfl-fcs {Classes?.Focused}" : string.Empty); @@ -229,26 +232,18 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - - if (firstRender is false || IsEnabled is false) return; - - if (AutoFocus) - { - await InputElement.FocusAsync(); - } - } - protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out string? result, [NotNullWhen(false)] out string? parsingErrorMessage) { - result = IsTrimmed ? value?.Trim() : value; + result = Trim ? value?.Trim() : value; parsingErrorMessage = null; return true; } + private void SetInputMode() + { + _inputMode = InputMode?.ToString().ToLower(); + } private void SetElementType() { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor index 40564cb513..095718fbb8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor @@ -9,13 +9,13 @@ @if (LabelTemplate is not null) { - <label for="@_buttonId"> + <label for="@_buttonId" @onclick="HandleOnClick"> @LabelTemplate </label> } else if (Label.HasValue()) { - <label style="@Styles?.Label" class="bit-tgl-lbl @Classes?.Label" for="@_buttonId"> + <label for="@_buttonId" style="@Styles?.Label" class="bit-tgl-lbl @Classes?.Label"> @Label </label> } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor.cs index 5c879a1238..4fff4dd852 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.razor.cs @@ -15,20 +15,26 @@ public partial class BitToggle : BitInputBase<bool> /// <summary> - /// Custom CSS classes for different parts of the BitToggle. + /// Custom CSS classes for different parts of the toggle. /// </summary> [Parameter] public BitToggleClassStyles? Classes { get; set; } /// <summary> - /// Default text used when the On or Off texts are null. + /// The default value of the toggle when the value parameter has not been assigned. /// </summary> - [Parameter] public string? DefaultText { get; set; } + [Parameter] public bool? DefaultValue { get; set; } /// <summary> - /// Whether the label (not the onText/offText) should be positioned inline with the toggle control. - /// Left (right in RTL) side when on/off text provided VS right (left in RTL) side when there is no on/off text. + /// Renders the toggle in full width of its container while putting space between the label and the knob. /// </summary> - [Parameter] public bool IsInlineLabel { get; set; } + [Parameter, ResetClassBuilder] + public bool FullWidth { get; set; } + + /// <summary> + /// Renders the label and the knob in a single line together. + /// </summary> + [Parameter, ResetClassBuilder] + public bool Inline { get; set; } /// <summary> /// Label of the toggle. @@ -62,10 +68,15 @@ public partial class BitToggle : BitInputBase<bool> [Parameter] public string? Role { get; set; } = "switch"; /// <summary> - /// Custom CSS styles for different parts of the BitToggle. + /// Custom CSS styles for different parts of the toggle. /// </summary> [Parameter] public BitToggleClassStyles? Styles { get; set; } + /// <summary> + /// The default text used when the On or Off texts are null. + /// </summary> + [Parameter] public string? Text { get; set; } + protected override string RootElementClass => "bit-tgl"; @@ -76,9 +87,11 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => CurrentValue ? $"bit-tgl-chk {Classes?.Checked}" : string.Empty); - ClassBuilder.Register(() => Reversed ? "bit-tgl-rvs" : string.Empty); + ClassBuilder.Register(() => FullWidth ? "bit-tgl-fwi" : string.Empty); - ClassBuilder.Register(() => IsInlineLabel ? "bit-tgl-inl" : string.Empty); + ClassBuilder.Register(() => Inline ? "bit-tgl-inl" : string.Empty); + + ClassBuilder.Register(() => Reversed ? "bit-tgl-rvs" : string.Empty); } protected override void RegisterCssStyles() @@ -94,6 +107,11 @@ protected override void OnInitialized() _buttonId = $"BitToggle-{UniqueId}-button"; _stateTextId = $"BitToggle-{UniqueId}-state-text"; + if (ValueHasBeenSet is false && DefaultValue is not null) + { + Value = DefaultValue.Value; + } + SetStateText(); OnValueChanged += HandleOnValueChanged; @@ -128,15 +146,14 @@ private void HandleOnValueChanged(object? sender, EventArgs args) private async Task HandleOnClick(MouseEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; CurrentValue = !CurrentValue; } private void SetStateText() { - _stateText = (CurrentValue ? OnText : OffText) ?? DefaultText; + _stateText = (CurrentValue ? OnText : OffText) ?? Text; if (AriaLabel.HasValue()) return; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss index 2b94b59080..272168857b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss @@ -1,8 +1,12 @@ @import "../../../Styles/functions.scss"; .bit-tgl { + display: flex; + gap: spacing(1); + width: fit-content; font-size: inherit; font-weight: inherit; + flex-direction: column; font-family: $tg-font-family; &.bit-dis { @@ -30,8 +34,8 @@ &.bit-tgl-chk { .bit-tgl-btn { - justify-content: flex-end; background: $clr-bg-dis; + justify-content: flex-end; border: $shp-border-width $shp-border-style transparent; } @@ -68,20 +72,33 @@ } } +.bit-tgl-inl { + align-items: center; + flex-direction: row; +} + +.bit-tgl-fwi { + width: 100%; + + &.bit-tgl-inl { + gap: 0; + } + + label { + flex-grow: 1; + } +} + .bit-tgl-lbl { - margin: 0; - box-shadow: none; + cursor: pointer; font-weight: 600; - display: inline-block; - box-sizing: border-box; - font-size: spacing(1.75); - padding: spacing(0.625) 0; - overflow-wrap: break-word; color: $clr-fg-pri; + font-size: spacing(1.75); } .bit-tgl-cnt { display: flex; + gap: spacing(1); position: relative; align-items: center; } @@ -97,10 +114,10 @@ min-width: spacing(5); box-sizing: border-box; font-size: spacing(2.5); + background: $clr-bg-pri; padding: 0 spacing(0.375); - border-radius: spacing(1.25); transition: all 0.1s ease 0s; - background: $clr-bg-pri; + border-radius: spacing(1.25); border: $shp-border-width $shp-border-style $clr-brd-pri; @media (hover: hover) { @@ -127,9 +144,9 @@ .bit-tgl-stx { padding: 0; + cursor: pointer; font-weight: 400; user-select: none; - margin: 0 spacing(1); } .bit-tgl-chk { @@ -156,13 +173,6 @@ } } -.bit-tgl-inl { - display: flex; - gap: spacing(2); - align-items: center; - flex-direction: row; -} - .bit-tgl-rvs { display: flex; flex-direction: column-reverse; @@ -170,4 +180,8 @@ &.bit-tgl-inl { flex-direction: row-reverse; } + + .bit-tgl-lbl { + text-align: end; + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/CircularTimePicker/BitCircularTimePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/CircularTimePicker/BitCircularTimePicker.razor.cs index dd628a83a7..513e02fe31 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/CircularTimePicker/BitCircularTimePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/CircularTimePicker/BitCircularTimePicker.razor.cs @@ -62,7 +62,7 @@ public partial class BitCircularTimePicker : BitInputBase<TimeSpan?>, IAsyncDisp /// CultureInfo for the TimePicker /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetCulture))] public CultureInfo? Culture { get; set; } /// <summary> @@ -330,8 +330,7 @@ private async Task CloseCallout() private async Task HandleOnChange(ChangeEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (AllowTextInput is false) return; CurrentValueAsString = e.Value?.ToString(); @@ -350,7 +349,7 @@ private async Task HandleOnClick() await OnClick.InvokeAsync(); } - private void HandleParameterChanges() + private void OnSetCulture() { _culture = Culture ?? CultureInfo.CurrentUICulture; } @@ -517,8 +516,7 @@ await _js.ToggleCallout(_dotnetObj, private async Task UpdateTime(MouseEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; var rect = await _js.GetBoundingClientRect(_clockRef); var radius = rect.Width / 2; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor index 021d3503a5..5f53830704 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor @@ -553,7 +553,7 @@ aria-disabled="@(IsEnabled is false)" style="@Styles?.TimePickerIncreaseHourButton" class="bit-dtp-tbt @Classes?.TimePickerIncreaseHourButton"> - <i style="@Styles?.TimePickerIncreaseHourIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.TimePickerIncreaseHourIcon" aria-hidden="true" /> + <i style="@Styles?.TimePickerIncreaseHourIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.TimePickerIncreaseHourIcon" aria-hidden="true" /> </button> <input @ref="_inputTimeHourRef" @bind="@_hourView" @@ -589,7 +589,7 @@ aria-disabled="@(IsEnabled is false)" style="@Styles?.TimePickerIncreaseMinuteButton" class="bit-dtp-tbt @Classes?.TimePickerIncreaseMinuteButton"> - <i style="@Styles?.TimePickerIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.TimePickerIncreaseMinuteIcon" aria-hidden="true" /> + <i style="@Styles?.TimePickerIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.TimePickerIncreaseMinuteIcon" aria-hidden="true" /> </button> <input @ref="_inputTimeMinuteRef" @bind="@_minuteView" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor.cs index 1b0c0a3d88..fca4ced81d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.razor.cs @@ -136,7 +136,7 @@ private int _minuteView /// CultureInfo for the DatePicker. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public CultureInfo? Culture { get; set; } /// <summary> @@ -277,14 +277,14 @@ private int _minuteView /// The maximum date allowed for the DatePicker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MaxDate { get; set; } /// <summary> /// The minimum date allowed for the DatePicker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MinDate { get; set; } /// <summary> @@ -346,14 +346,14 @@ private int _minuteView /// Show month picker on top of date picker when visible. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowMonthPickerAsOverlay { get; set; } /// <summary> /// Whether or not render the time-picker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowTimePicker { get; set; } /// <summary> @@ -400,7 +400,7 @@ private int _minuteView /// Show month picker on top of date picker when visible. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowTimePickerAsOverlay { get; set; } /// <summary> @@ -422,14 +422,14 @@ private int _minuteView /// Specifies the date and time of the date-picker when it is opened without any selected value. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? StartingValue { get; set; } /// <summary> /// Whether the date-picker is rendered standalone or with the input component and callout. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool Standalone { get; set; } @@ -489,7 +489,7 @@ protected override void OnInitialized() OnValueChanged += HandleOnValueChanged; - HandleParameterChanges(); + OnSetParameters(); base.OnInitialized(); } @@ -605,8 +605,7 @@ private async Task HandleOnFocus() private async Task HandleOnChange(ChangeEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (AllowTextInput is false) return; var oldValue = CurrentValue.GetValueOrDefault(DateTimeOffset.Now); @@ -643,10 +642,10 @@ private async Task HandleOnClearButtonClick() private void HandleOnValueChanged(object? sender, EventArgs args) { - HandleParameterChanges(); + OnSetParameters(); } - private void HandleParameterChanges() + private void OnSetParameters() { _culture = Culture ?? CultureInfo.CurrentUICulture; @@ -693,8 +692,7 @@ private void HandleParameterChanges() private async Task SelectDate(int dayIndex, int weekIndex) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (IsOpenHasBeenSet && IsOpenChanged.HasDelegate is false) return; if (IsWeekDayOutOfMinAndMaxDate(dayIndex, weekIndex)) return; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.scss index 6a63a2e750..eac76ee7e7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DatePicker/BitDatePicker.scss @@ -684,17 +684,17 @@ .bit-dtp-tpr { display: flex; - flex-direction: column; + gap: spacing(0.25); align-items: center; + flex-direction: column; justify-content: space-between; - gap: spacing(0.25); } .bit-dtp-tdv { display: flex; - justify-content: center; align-items: center; font-size: spacing(2.5); + justify-content: center; font-weight: $tg-font-weight; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor index 87d7267648..6d5297b4b5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor @@ -547,7 +547,7 @@ class="bit-dtrp-tbt @Classes?.StartTimeIncreaseHourButton" disabled="@startTimeIncreaseHourDisabled" aria-disabled="@startTimeIncreaseHourDisabled"> - <i style="@Styles?.StartTimeIncreaseHourIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.StartTimeIncreaseHourIcon" aria-hidden="true" /> + <i style="@Styles?.StartTimeIncreaseHourIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.StartTimeIncreaseHourIcon" aria-hidden="true" /> </button> <input @ref="_startTimeHourInputRef" @bind="@_startTimeHourView" @@ -583,7 +583,7 @@ class="bit-dtrp-tbt @Classes?.StartTimeIncreaseMinuteButton" disabled="@startTimeIncreaseMinuteDisabled" aria-disabled="@startTimeIncreaseMinuteDisabled"> - <i style="@Styles?.StartTimeIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.StartTimeIncreaseMinuteIcon" aria-hidden="true" /> + <i style="@Styles?.StartTimeIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.StartTimeIncreaseMinuteIcon" aria-hidden="true" /> </button> <input @ref="_startTimeMinuteInputRef" @bind="@_startTimeMinuteView" @@ -648,7 +648,7 @@ class="bit-dtrp-tbt @Classes?.EndTimeIncreaseHourButton" disabled="@endTimeIncreaseHourDisabled" aria-disabled="@endTimeIncreaseHourDisabled"> - <i style="@Styles?.EndTimeIncreaseHourIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.EndTimeIncreaseHourIcon" aria-hidden="true" /> + <i style="@Styles?.EndTimeIncreaseHourIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.EndTimeIncreaseHourIcon" aria-hidden="true" /> </button> <input @ref="_endTimeHourInputRef" @bind="@_endTimeHourView" @@ -684,7 +684,7 @@ class="bit-dtrp-tbt @Classes?.EndTimeIncreaseMinuteButton" disabled="@endTimeIncreaseMinuteDisabled" aria-disabled="@endTimeIncreaseMinuteDisabled"> - <i style="@Styles?.EndTimeIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.EndTimeIncreaseMinuteIcon" aria-hidden="true" /> + <i style="@Styles?.EndTimeIncreaseMinuteIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.EndTimeIncreaseMinuteIcon" aria-hidden="true" /> </button> <input @ref="_endTimeMinuteInputRef" @bind="@_endTimeMinuteView" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor.cs index 07d9fb87dd..7227366e8a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.razor.cs @@ -212,7 +212,7 @@ private int _endTimeMinuteView /// CultureInfo for the DateRangePicker. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public CultureInfo? Culture { get; set; } /// <summary> @@ -343,14 +343,14 @@ private int _endTimeMinuteView /// The maximum date allowed for the DateRangePicker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MaxDate { get; set; } /// <summary> /// The minimum date allowed for the DateRangePicker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public DateTimeOffset? MinDate { get; set; } /// <summary> @@ -402,14 +402,14 @@ private int _endTimeMinuteView /// Show month picker on top of date range picker when visible. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowMonthPickerAsOverlay { get; set; } /// <summary> /// Whether or not render the time-picker. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowTimePicker { get; set; } /// <summary> @@ -461,7 +461,7 @@ private int _endTimeMinuteView /// Show month picker on top of date range picker when visible. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool ShowTimePickerAsOverlay { get; set; } /// <summary> @@ -488,14 +488,14 @@ private int _endTimeMinuteView /// Specifies the date and time of the date and time picker when it is opened without any selected value. /// </summary> [Parameter] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public BitDateRangePickerValue? StartingValue { get; set; } /// <summary> /// Whether the DateRangePicker is rendered standalone or with the input component and callout. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetParameters))] public bool Standalone { get; set; } @@ -555,7 +555,7 @@ protected override void OnInitialized() OnValueChanged += HandleOnValueChanged; - HandleParameterChanges(); + OnSetParameters(); base.OnInitialized(); } @@ -683,8 +683,7 @@ private async Task HandleOnFocus() private async Task HandleOnChange(ChangeEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (AllowTextInput is false) return; CurrentValueAsString = e.Value?.ToString(); @@ -712,10 +711,10 @@ private async Task HandleOnClearButtonClick() private void HandleOnValueChanged(object? sender, EventArgs args) { - HandleParameterChanges(); + OnSetParameters(); } - private void HandleParameterChanges() + private void OnSetParameters() { _culture = Culture ?? CultureInfo.CurrentUICulture; @@ -793,8 +792,7 @@ private void HandleParameterChanges() private async Task SelectDate(int dayIndex, int weekIndex) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (IsOpenHasBeenSet && IsOpenChanged.HasDelegate is false) return; if (IsWeekDayOutOfMinAndMaxDate(dayIndex, weekIndex)) return; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.scss index 88bb2db206..3271a078d0 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/DateRangePicker/BitDateRangePicker.scss @@ -718,19 +718,19 @@ .bit-dtrp-tpr { display: flex; - flex-direction: column; - align-content: center; + gap: spacing(0.25); align-items: center; + align-content: center; + flex-direction: column; justify-content: space-between; - gap: spacing(0.25); } .bit-dtrp-tdv { display: flex; - justify-content: center; - align-content: center; align-items: center; + align-content: center; font-size: spacing(2.5); + justify-content: center; font-weight: $tg-font-weight; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor index 97fbde24c9..8093236028 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor @@ -85,7 +85,7 @@ style="@Styles?.IncreaseHourButton" class="bit-tpc-tbt @Classes?.IncreaseHourButton" disabled="@(IsEnabled is false)"> - <i style="@Styles?.IncreaseHourIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.IncreaseHourIcon" aria-hidden="true" /> + <i style="@Styles?.IncreaseHourIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.IncreaseHourIcon" aria-hidden="true" /> </button> <input @ref="_inputHourRef" @bind="@_hourView" @@ -118,7 +118,7 @@ style="@Styles?.IncreaseMinuteButton" class="bit-tpc-tbt @Classes?.IncreaseMinuteButton" disabled="@(IsEnabled is false)"> - <i style="@Styles?.IncreaseMinuteIcon" class="bit-icon bit-icon--ChevronUpSmall @Classes?.IncreaseMinuteIcon" aria-hidden="true" /> + <i style="@Styles?.IncreaseMinuteIcon" class="bit-icon bit-icon--ChevronDownSmall bit-ico-r180 @Classes?.IncreaseMinuteIcon" aria-hidden="true" /> </button> <input @ref="_inputMinuteRef" @bind="@_minuteView" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor.cs index 8e066dda34..61630b71ea 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/_Pickers/TimePicker/BitTimePicker.razor.cs @@ -122,7 +122,7 @@ private string? _minuteView /// CultureInfo for the TimePicker /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(HandleParameterChanges))] + [CallOnSet(nameof(OnSetCulture))] public CultureInfo? Culture { get; set; } /// <summary> @@ -408,8 +408,7 @@ await _js.ToggleCallout(_dotnetObj, private async Task HandleOnChange(ChangeEventArgs e) { - if (IsEnabled is false) return; - if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return; + if (IsEnabled is false || InvalidValueBinding()) return; if (AllowTextInput is false) return; CurrentValueAsString = e.Value?.ToString(); @@ -428,7 +427,7 @@ private async Task HandleOnClick() await OnClick.InvokeAsync(); } - private void HandleParameterChanges() + private void OnSetCulture() { _culture = Culture ?? CultureInfo.CurrentUICulture; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.cs b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.cs index e322c3b4f8..12be9f1149 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.cs @@ -4,6 +4,12 @@ namespace Bit.BlazorUI; public partial class BitStack : BitComponentBase { + /// <summary> + /// Defines whether to render Stack children both horizontally and vertically. + /// </summary> + [Parameter, ResetStyleBuilder] + public BitAlignment? Alignment { get; set; } + /// <summary> /// Make the height of the stack auto. /// </summary> @@ -32,6 +38,30 @@ public partial class BitStack : BitComponentBase /// </summary> [Parameter] public string? Element { get; set; } + /// <summary> + /// Expand the direct children to occupy all of the root element's width. + /// </summary> + [Parameter, ResetClassBuilder] + public bool FillContent { get; set; } + + /// <summary> + /// Sets the height of the stack to fit its content. + /// </summary> + [Parameter, ResetStyleBuilder] + public bool FitHeight { get; set; } + + /// <summary> + /// Sets the width and height of the stack to fit its content. + /// </summary> + [Parameter, ResetStyleBuilder] + public bool FitSize { get; set; } + + /// <summary> + /// Sets the width of the stack to fit its content. + /// </summary> + [Parameter, ResetStyleBuilder] + public bool FitWidth { get; set; } + /// <summary> /// Defines the spacing between Stack children. /// </summary> @@ -84,6 +114,11 @@ public partial class BitStack : BitComponentBase protected override string RootElementClass => "bit-stc"; + protected override void RegisterCssClasses() + { + ClassBuilder.Register(() => FillContent ? "bit-stc-fcn" : string.Empty); + } + protected override void RegisterCssStyles() { StyleBuilder.Register(() => $"flex-direction:{(Horizontal ? "row" : "column")}{(Reversed ? "-reverse" : string.Empty)}"); @@ -91,14 +126,24 @@ protected override void RegisterCssStyles() StyleBuilder.Register(() => $"gap:{Gap ?? "1rem"}"); StyleBuilder.Register(() => - (Horizontal && VerticalAlign is not null) || (Horizontal is false && HorizontalAlign is not null) - ? $"align-items:{_AlignmentMap[Horizontal ? VerticalAlign!.Value : HorizontalAlign!.Value]}" - : string.Empty); + { + var vAlign = VerticalAlign ?? Alignment; + var hAlign = HorizontalAlign ?? Alignment; + + return (Horizontal && vAlign is not null) || (Horizontal is false && hAlign is not null) + ? $"align-items:{_AlignmentMap[Horizontal ? vAlign!.Value : hAlign!.Value]}" + : string.Empty; + }); StyleBuilder.Register(() => - (Horizontal && HorizontalAlign is not null) || (Horizontal is false && VerticalAlign is not null) - ? $"justify-content:{_AlignmentMap[Horizontal ? HorizontalAlign!.Value : VerticalAlign!.Value]}" - : string.Empty); + { + var vAlign = VerticalAlign ?? Alignment; + var hAlign = HorizontalAlign ?? Alignment; + + return (Horizontal && hAlign is not null) || (Horizontal is false && vAlign is not null) + ? $"justify-content:{_AlignmentMap[Horizontal ? hAlign!.Value : vAlign!.Value]}" + : string.Empty; + }); StyleBuilder.Register(() => (Grow.HasValue() || Grows) ? $"flex-grow:{(Grow.HasValue() ? Grow : "1")}" : string.Empty); @@ -106,6 +151,9 @@ protected override void RegisterCssStyles() StyleBuilder.Register(() => (AutoSize || AutoWidth) ? "width:auto" : string.Empty); StyleBuilder.Register(() => (AutoSize || AutoHeight) ? "height:auto" : string.Empty); + + StyleBuilder.Register(() => (FitSize || FitWidth) ? "width:fit-content" : string.Empty); + StyleBuilder.Register(() => (FitSize || FitHeight) ? "height:fit-content" : string.Empty); } protected override void BuildRenderTree(RenderTreeBuilder builder) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.scss b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.scss index 33fc89e0e3..d2983af935 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Stack/BitStack.scss @@ -4,7 +4,13 @@ width: 100%; height: 100%; display: flex; - align-items: center; box-sizing: border-box; - justify-content: center; + align-items: flex-start; + justify-content: flex-start; } + +.bit-stc-fcn { + > * { + width: 100%; + } +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor index aa31ff8940..d5326aff5a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor @@ -27,7 +27,7 @@ </div> <div class="bit-csl-rbt" @onclick="GoRight" style="@_goRightButtonStyle"> - <i class="bit-icon bit-icon--ChevronLeft" /> + <i class="bit-csl-rbi bit-icon bit-icon--ChevronRight" /> </div> } </div> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.scss b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.scss index b82c835063..bdd8606c14 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.scss @@ -67,6 +67,10 @@ left: 0; } +.bit-csl-rbi { + transform: scaleX(-1); +} + .bit-csl-dot { text-align: center; margin-top: spacing(1.25); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.razor b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.razor index 560fb61fbe..09bd6a0aeb 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.razor @@ -22,7 +22,7 @@ </div> <div class="bit-swp-lbt" @onclick="async () => await Go(false)" style="@_leftButtonStyle" @onpointerdown:stopPropagation> - <i class="bit-icon bit-icon--ChevronLeft" /> + <i class="bit-swp-lbi bit-icon bit-icon--ChevronRight" /> </div> } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.scss b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.scss index a5cda76143..424ab42b0e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.scss @@ -61,6 +61,10 @@ left: 0; } +.bit-swp-lbi { + transform: scaleX(-1); +} + .bit-swp-dot { text-align: center; margin-top: spacing(1.25); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor index 28966a3037..e13275a329 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor @@ -3,7 +3,7 @@ @typeparam TItem <CascadingValue Value="this" IsFixed="true"> - <div style="display:none" hidden>@ChildContent</div> + <div style="display:none" hidden>@(Options ?? ChildContent)</div> </CascadingValue> <div @ref="RootElement" @attributes="HtmlAttributes" @@ -14,82 +14,214 @@ aria-label="@AriaLabel"> <div @onclick="CloseCallout" @onclick:stopPropagation - style="display:@(_isCalloutOpen ? "block" : "none")" - class="bit-brc-ovl"></div> + style="display:@(_isCalloutOpen ? "block" : "none");@Styles?.Overlay" + class="bit-brc-ovl @Classes?.Overlay"></div> - <div class="bit-brc-iwp"> - <ul> - @foreach ((TItem item, int index) in _displayItems.Select((item, index) => (item, index))) - { - <li @key="GetKey(item)"> - @if (_overflowItems.Any() && index == _internalOverflowIndex) - { - <button id="@_overflowAnchorId" - type="button" - class="bit-brc-obt" - aria-label="@OverflowAriaLabel" @onclick="OpenCallout"> - <span class="bit-brc-oic"> - <i class="bit-icon bit-icon--@OverflowIconName" /> - </span> - </button> - } - else - { - if (GetItemHref(item).HasValue()) + <ul style="@Styles?.ItemContainer" class="bit-brc-icn @Classes?.ItemContainer"> + @foreach ((TItem item, int index) in _displayItems.Select((item, index) => (item, index))) + { + <li style="@Styles?.ItemWrapper" class="@Classes?.ItemWrapper" @key="GetKey(item)"> + @if (index == _internalOverflowIndex && _overflowItems.Any()) + { + <button id="@_overflowAnchorId" + type="button" + style="@Styles?.OverflowButton" + class="bit-brc-obt @Classes?.OverflowButton" + aria-label="@OverflowAriaLabel" @onclick="OpenCallout" + disabled="@(IsEnabled is false)"> + @if (OverflowIconTemplate is not null) { - <a href="@GetItemHref(item)" - aria-current="@(GetIsSelected(item) ? "page" : null)" - style="@GetStyles(item)" - class="bit-brc-itm @GetClasses(item)"> - @GetItemText(item) - </a> + @OverflowIconTemplate } else { - <button type="button" - aria-current="@(GetIsSelected(item) ? "page" : null)" - style="@GetStyles(item)" - class="bit-brc-itm @GetClasses(item)" @onclick="() => HandleOnItemClick(item)"> - @GetItemText(item) - </button> + <i style="@Styles?.OverflowButtonIcon" class="bit-icon bit-icon--@(OverflowIconName ?? "More") @Classes?.OverflowButtonIcon" /> } - } - </li> - - @if (index != _displayItems.Count - 1) + </button> + } + else { - <i class="bit-brc-sep bit-icon bit-icon--@(DividerIconName ?? _internalDividerIconName)" /> + var template = GetTemplate(item); + if (GetItemHref(item).HasValue()) + { + <a href="@GetItemHref(item)" + aria-current="@(GetIsSelected(item) ? "page" : null)" + style="@GetStyles(item, false)" + class="bit-brc-itm @GetClasses(item, false)"> + @if (template is not null) + { + @template(item) + } + else if (ItemTemplate is not null) + { + @ItemTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.ItemIcon" class="bit-icon bit-icon--@iconName @Classes?.ItemIcon" /> + } + <span style="@Styles?.ItemText" class="@Classes?.ItemText">@GetItemText(item)</span> + } + </a> + } + else if (OnItemClick.HasDelegate) + { + <button type="button" + aria-current="@(GetIsSelected(item) ? "page" : null)" + disabled="@(GetIsEnabled(item) is false)" + style="@GetStyles(item, false)" + class="bit-brc-itm @GetClasses(item, true)" @onclick="() => HandleOnItemClick(item)"> + @if (template is not null) + { + @template(item) + } + else if (ItemTemplate is not null) + { + @ItemTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.ItemIcon" class="bit-icon bit-icon--@iconName @Classes?.ItemIcon" /> + } + <span style="@Styles?.ItemText" class="@Classes?.ItemText">@GetItemText(item)</span> + } + </button> + } + else + { + <span aria-current="@(GetIsSelected(item) ? "page" : null)" + style="@GetStyles(item, false)" + class="bit-brc-nii @GetClasses(item, false)"> + @if (template is not null) + { + @template(item) + } + else if (ItemTemplate is not null) + { + @ItemTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.ItemIcon" class="bit-icon bit-icon--@iconName @Classes?.ItemIcon" /> + } + <span style="@Styles?.ItemText" class="@Classes?.ItemText">@GetItemText(item)</span> + } + </span> + } } + </li> + + @if (index != _displayItems.Count - 1) + { + <li style="@Styles?.Divider" class="@Classes?.Divider"> + @if (DividerIconTemplate is not null) + { + @DividerIconTemplate + } + else + { + <i style="@Styles?.DividerIcon" + class="bit-brc-div bit-icon bit-icon--@(DividerIconName ?? "ChevronRight bit-brc-trd") @Classes?.DividerIcon" /> + } + </li> } - </ul> - </div> + } + </ul> <div id="@_calloutId" tabindex="0" role="listbox" - class="bit-brc-cal"> - <ul id="@_scrollContainerId" class="bit-brc-scn"> + style="@Styles?.Callout" + class="bit-brc-cal @Classes?.Callout"> + <ul style="@Styles?.CalloutContainer" id="@_scrollContainerId" class="bit-brc-scn @Classes?.CalloutContainer"> @foreach (var item in _overflowItems) { - <li @key="GetKey(item)"> + var overflowTemplate = GetOverflowTemplate(item); + <li style="@Styles?.OverflowItemWrapper" class="@Classes?.OverflowItemWrapper" @key="GetKey(item)"> @if (GetItemHref(item).HasValue()) { <a href="@GetItemHref(item)" aria-current="@(GetIsSelected(item) ? "page" : null)" - style="@GetStyles(item)" - class="bit-brc-oitm @GetClasses(item)"> - @GetItemText(item) + style="@GetStyles(item, true)" + class="bit-brc-ofi @GetClasses(item, true)"> + @if (overflowTemplate is not null) + { + @overflowTemplate(item) + } + else if (OverflowTemplate is not null) + { + @OverflowTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.OverflowItemIcon" class="bit-icon bit-icon--@iconName @Classes?.OverflowItemIcon" /> + } + <span style="@Styles?.OverflowItemText" class="@Classes?.OverflowItemText">@GetItemText(item)</span> + } </a> } - else + else if (OnItemClick.HasDelegate) { <button type="button" aria-current="@(GetIsSelected(item) ? "page" : null)" - style="@GetStyles(item)" - class="bit-brc-oitm @GetClasses(item)" @onclick="() => HandleOnItemClick(item)"> - @GetItemText(item) + disabled="@(GetIsEnabled(item) is false)" + style="@GetStyles(item, true)" + class="bit-brc-ofi @GetClasses(item, true)" @onclick="() => HandleOnItemClick(item)"> + @if (overflowTemplate is not null) + { + @overflowTemplate(item) + } + else if (OverflowTemplate is not null) + { + @OverflowTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.OverflowItemIcon" class="bit-icon bit-icon--@iconName @Classes?.OverflowItemIcon" /> + } + <span style="@Styles?.OverflowItemText" class="@Classes?.OverflowItemText">@GetItemText(item)</span> + } </button> } + else + { + <span aria-current="@(GetIsSelected(item) ? "page" : null)" + style="@GetStyles(item, true)" + class="bit-brc-ofn @GetClasses(item, true)"> + @if (overflowTemplate is not null) + { + @overflowTemplate(item) + } + else if (OverflowTemplate is not null) + { + @OverflowTemplate(item) + } + else + { + var iconName = GetIconName(item); + @if (iconName.HasValue()) + { + <i style="@Styles?.OverflowItemIcon" class="bit-icon bit-icon--@iconName @Classes?.OverflowItemIcon" /> + } + <span style="@Styles?.OverflowItemText" class="@Classes?.OverflowItemText">@GetItemText(item)</span> + } + </span> + } </li> } </ul> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs index 9d37269926..cb46593227 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs @@ -10,7 +10,6 @@ public partial class BitBreadcrumb<TItem> : BitComponentBase, IAsyncDisposable w private List<TItem> _internalItems = []; private List<TItem> _displayItems = []; private List<TItem> _overflowItems = []; - private string _internalDividerIconName = default!; private DotNetObjectReference<BitBreadcrumb<TItem>> _dotnetObj = default!; private string _calloutId = default!; @@ -26,17 +25,36 @@ public partial class BitBreadcrumb<TItem> : BitComponentBase, IAsyncDisposable w /// <summary> /// The content of the BitBreadcrumb, that are BitBreadOption components. /// </summary> - [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public RenderFragment? ChildContent { get; set; } + + /// <summary> + /// Custom CSS classes for different parts of the breadcrumb. + /// </summary> + [Parameter] public BitBreadcrumbClassStyles? Classes { get; set; } /// <summary> /// Render a custom divider in place of the default chevron > /// </summary> [Parameter] public string? DividerIconName { get; set; } + /// <summary> + /// The custom template content to render divider icon. + /// </summary> + [Parameter] public RenderFragment? DividerIconTemplate { get; set; } + /// <summary> /// Collection of BreadLists to render. /// </summary> - [Parameter] public IList<TItem> Items { get; set; } = []; + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public IList<TItem> Items { get; set; } = []; + + /// <summary> + /// The custom template content to render each item. + /// </summary> + [Parameter] public RenderFragment<TItem>? ItemTemplate { get; set; } /// <summary> /// The maximum number of BreadLists to display before coalescing. @@ -49,6 +67,18 @@ public partial class BitBreadcrumb<TItem> : BitComponentBase, IAsyncDisposable w /// </summary> [Parameter] public BitBreadcrumbNameSelectors<TItem>? NameSelectors { get; set; } + /// <summary> + /// Callback for when the BreadList item clicked. + /// </summary> + [Parameter] public EventCallback<TItem> OnItemClick { get; set; } + + /// <summary> + /// Alias of the ChildContent. + /// </summary> + [Parameter] + [CallOnSet(nameof(OnSetParameters))] + public RenderFragment? Options { get; set; } + /// <summary> /// Aria label for the overflow button. /// </summary> @@ -62,22 +92,27 @@ public partial class BitBreadcrumb<TItem> : BitComponentBase, IAsyncDisposable w /// <summary> /// Render a custom overflow icon in place of the default icon. /// </summary> - [Parameter] public string OverflowIconName { get; set; } = "More"; + [Parameter] public string? OverflowIconName { get; set; } /// <summary> - /// Callback for when the BreadList item clicked. + /// The custom template content to render each overflow icon. /// </summary> - [Parameter] public EventCallback<TItem> OnItemClick { get; set; } + [Parameter] public RenderFragment? OverflowIconTemplate { get; set; } + + /// <summary> + /// The custom template content to render each item in overflow list. + /// </summary> + [Parameter] public RenderFragment<TItem>? OverflowTemplate { get; set; } /// <summary> - /// The class HTML attribute for Selected Item. + /// Reverses the positions of the icon and the item text of the item content. /// </summary> - [Parameter] public string? SelectedItemClass { get; set; } + [Parameter] public bool ReversedIcon { get; set; } /// <summary> - /// The style HTML attribute for Selected Item. + /// Custom CSS styles for different parts of the breadcrumb. /// </summary> - [Parameter] public string? SelectedItemStyle { get; set; } + [Parameter] public BitBreadcrumbClassStyles? Styles { get; set; } @@ -112,6 +147,16 @@ internal void UnregisterOptions(BitBreadcrumbOption option) protected override string RootElementClass => "bit-brc"; + protected override void RegisterCssClasses() + { + ClassBuilder.Register(() => Classes?.Root); + } + + protected override void RegisterCssStyles() + { + StyleBuilder.Register(() => Styles?.Root); + } + protected override Task OnInitializedAsync() { _calloutId = $"BitBreadcrumb-{UniqueId}-callout"; @@ -120,38 +165,9 @@ protected override Task OnInitializedAsync() _dotnetObj = DotNetObjectReference.Create(this); - return base.OnInitializedAsync(); - } - - protected override async Task OnParametersSetAsync() - { - _internalDividerIconName = Dir == BitDir.Rtl ? "ChevronLeft" : "ChevronRight"; - - if (ChildContent is null) - { - _items = [.. Items]; - } - - if (_items.Any()) - { - bool shouldCallSetItemsToShow = false; - - shouldCallSetItemsToShow = _internalItems.Count != _items.Count || _internalItems.Any(item => _items.Contains(item) is false); - _internalItems = [.. _items]; - - shouldCallSetItemsToShow = shouldCallSetItemsToShow || _internalMaxDisplayedItems != MaxDisplayedItems; - _internalMaxDisplayedItems = MaxDisplayedItems == 0 ? (uint)_internalItems.Count : MaxDisplayedItems; - - shouldCallSetItemsToShow = shouldCallSetItemsToShow || _internalOverflowIndex != OverflowIndex; - _internalOverflowIndex = OverflowIndex >= _internalMaxDisplayedItems ? 0 : OverflowIndex; - - if (shouldCallSetItemsToShow) - { - SetItemsToShow(); - } - } + OnSetParameters(); - await base.OnParametersSetAsync(); + return base.OnInitializedAsync(); } @@ -160,6 +176,7 @@ private async Task HandleOnItemClick(TItem item) { if (IsEnabled is false) return; if (GetIsEnabled(item) is false) return; + if (OnItemClick.HasDelegate is false) return; await OnItemClick.InvokeAsync(item); @@ -177,6 +194,29 @@ private async Task HandleOnItemClick(TItem item) } } + private void OnSetParameters() + { + if (ChildContent is null && Options is null) + { + _items = [.. Items]; + } + + if (_items.Any() is false) return; + + bool shouldCallSetItemsToShow = _internalItems.Count != _items.Count || _internalItems.Any(item => _items.Contains(item) is false); + _internalItems = [.. _items]; + + shouldCallSetItemsToShow = shouldCallSetItemsToShow || _internalMaxDisplayedItems != MaxDisplayedItems; + _internalMaxDisplayedItems = MaxDisplayedItems == 0 ? (uint)_internalItems.Count : MaxDisplayedItems; + + shouldCallSetItemsToShow = shouldCallSetItemsToShow || _internalOverflowIndex != OverflowIndex; + _internalOverflowIndex = OverflowIndex >= _internalMaxDisplayedItems ? 0 : OverflowIndex; + + if (shouldCallSetItemsToShow is false) return; + + SetItemsToShow(); + } + private void SetItemsToShow() { _displayItems.Clear(); @@ -184,7 +224,7 @@ private void SetItemsToShow() if (_internalMaxDisplayedItems >= _internalItems.Count) { - _displayItems = _internalItems.ToList(); + _displayItems = [.. _internalItems]; return; } @@ -208,7 +248,29 @@ private void SetItemsToShow() } } - private string GetClasses(TItem item) + private string? GetKey(TItem item) + { + if (item is BitBreadcrumbItem breadcrumbItem) + { + return breadcrumbItem.Key; + } + + if (item is BitBreadcrumbOption bitBreadcrumbOption) + { + return bitBreadcrumbOption.Key; + } + + if (NameSelectors is null) return null; + + if (NameSelectors.Key.Selector is not null) + { + return NameSelectors.Key.Selector!(item); + } + + return item.GetValueFromProperty<string?>(NameSelectors.Key.Name); + } + + private string GetClasses(TItem item, bool isOverflowItem) { var classes = new List<string>(); @@ -222,60 +284,85 @@ private string GetClasses(TItem item) classes.Add("bit-brc-sel"); } - if (GetIsSelected(item) && SelectedItemClass.HasValue()) + if (isOverflowItem) { - classes.Add(SelectedItemClass!); - } + if (Classes?.OverflowItem.HasValue() ?? false) + { + classes.Add(Classes.OverflowItem!); + } - if (GetIsEnabled(item) is false) - { - classes.Add("bit-brc-disi"); + if (GetIsSelected(item) && (Classes?.OverflowSelectedItem.HasValue() ?? false)) + { + classes.Add(Classes.OverflowSelectedItem!); + } } - - return string.Join(" ", classes); - } - - private string? GetKey(TItem item) - { - if (item is BitBreadcrumbItem breadcrumbItem) + else { - return breadcrumbItem.Key; + if (Classes?.Item.HasValue() ?? false) + { + classes.Add(Classes.Item!); + } + + if (GetIsSelected(item) && (Classes?.SelectedItem.HasValue() ?? false)) + { + classes.Add(Classes.SelectedItem!); + } } - if (item is BitBreadcrumbOption bitBreadcrumbOption) + if (GetIsEnabled(item) is false) { - return bitBreadcrumbOption.Key; + classes.Add("bit-brc-dis"); } - if (NameSelectors is null) return null; - - if (NameSelectors.Key.Selector is not null) + if (GetReversedIcon(item)) { - return NameSelectors.Key.Selector!(item); + classes.Add("bit-brc-rvi"); } - return item.GetValueFromProperty<string?>(NameSelectors.Key.Name); + return string.Join(" ", classes); } - private string GetStyles(TItem item) + private string GetStyles(TItem item, bool isOverflowItem) { var styles = new List<string>(); if (GetItemStyle(item).HasValue()) { - styles.Add(GetItemStyle(item)!); + styles.Add(GetItemStyle(item)!.Trim(';')); } - if (GetIsSelected(item) && SelectedItemStyle.HasValue()) + if (isOverflowItem) + { + if (Styles?.OverflowItem.HasValue() ?? false) + { + styles.Add(Styles.OverflowItem!.Trim(';')); + } + + if (GetIsSelected(item) && (Styles?.OverflowSelectedItem.HasValue() ?? false)) + { + styles.Add(Styles.OverflowSelectedItem!.Trim(';')); + } + } + else { - styles.Add(SelectedItemStyle!); + if (Styles?.Item.HasValue() ?? false) + { + styles.Add(Styles.Item!.Trim(';')); + } + + if (GetIsSelected(item) && (Styles?.SelectedItem.HasValue() ?? false)) + { + styles.Add(Styles.SelectedItem!.Trim(';')); + } } - return string.Join(" ", styles); + return string.Join(';', styles); } private string? GetItemHref(TItem item) { + if (GetIsEnabled(item) is false) return null; + if (item is BitBreadcrumbItem breadcrumbItem) { return breadcrumbItem.Href; @@ -362,6 +449,50 @@ private string GetStyles(TItem item) return item.GetValueFromProperty<string?>(NameSelectors.Text.Name); } + private string? GetIconName(TItem item) + { + if (item is BitBreadcrumbItem breadcrumbItem) + { + return breadcrumbItem.IconName; + } + + if (item is BitBreadcrumbOption bitBreadcrumbOption) + { + return bitBreadcrumbOption.IconName; + } + + if (NameSelectors is null) return null; + + if (NameSelectors.IconName.Selector is not null) + { + return NameSelectors.IconName.Selector!(item); + } + + return item.GetValueFromProperty<string?>(NameSelectors.IconName.Name); + } + + private bool GetReversedIcon(TItem item) + { + if (item is BitBreadcrumbItem breadcrumbItem) + { + return breadcrumbItem.ReversedIcon ?? ReversedIcon; + } + + if (item is BitBreadcrumbOption bitBreadcrumbOption) + { + return bitBreadcrumbOption.ReversedIcon ?? ReversedIcon; + } + + if (NameSelectors is null) return true; + + if (NameSelectors.ReversedIcon.Selector is not null) + { + return NameSelectors.ReversedIcon.Selector!(item) ?? ReversedIcon; + } + + return item.GetValueFromProperty(NameSelectors.ReversedIcon.Name, ReversedIcon); + } + private bool GetIsSelected(TItem item) { if (item is BitBreadcrumbItem breadcrumbItem) @@ -386,6 +517,8 @@ private bool GetIsSelected(TItem item) private bool GetIsEnabled(TItem item) { + if (IsEnabled is false) return false; + if (item is BitBreadcrumbItem breadcrumbItem) { return breadcrumbItem.IsEnabled; @@ -406,6 +539,54 @@ private bool GetIsEnabled(TItem item) return item.GetValueFromProperty(NameSelectors.IsEnabled.Name, true); } + private RenderFragment<TItem>? GetOverflowTemplate(TItem? item) + { + if (item is null) return null; + + if (item is BitBreadcrumbItem breadcrumbItem) + { + return breadcrumbItem.OverflowTemplate as RenderFragment<TItem>; + } + + if (item is BitBreadcrumbOption bitBreadcrumbOption) + { + return bitBreadcrumbOption.OverflowTemplate as RenderFragment<TItem>; + } + + if (NameSelectors is null) return null; + + if (NameSelectors.OverflowTemplate.Selector is not null) + { + return NameSelectors.OverflowTemplate.Selector!(item); + } + + return item.GetValueFromProperty<RenderFragment<TItem>?>(NameSelectors.OverflowTemplate.Name); + } + + private RenderFragment<TItem>? GetTemplate(TItem? item) + { + if (item is null) return null; + + if (item is BitBreadcrumbItem breadcrumbItem) + { + return breadcrumbItem.Template as RenderFragment<TItem>; + } + + if (item is BitBreadcrumbOption bitBreadcrumbOption) + { + return bitBreadcrumbOption.Template as RenderFragment<TItem>; + } + + if (NameSelectors is null) return null; + + if (NameSelectors.Template.Selector is not null) + { + return NameSelectors.Template.Selector!(item); + } + + return item.GetValueFromProperty<RenderFragment<TItem>?>(NameSelectors.Template.Name); + } + private async Task OpenCallout() { _isCalloutOpen = true; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss index edd0f3fe68..68fba198dd 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss @@ -2,12 +2,13 @@ .bit-brc { white-space: nowrap; + --bit-brc-div-transform: scaleX(1); &.bit-dis { cursor: default; font-weight: 400; - pointer-events: none; color: $clr-fg-dis; + pointer-events: none; .bit-brc-obt, .bit-brc-itm, @@ -15,24 +16,23 @@ i { cursor: default; font-weight: 400; - pointer-events: none; color: $clr-fg-dis; + pointer-events: none; } } } -.bit-brc-iwp { - ul { - padding-inline-start: 0; +.bit-brc-icn { + padding-inline-start: 0; - li { - align-items: center; - display: inline-flex; + li { + align-items: center; + display: inline-flex; + } - a { - text-decoration: none; - } - } + a { + text-decoration: none; + border-radius: $shp-border-radius; } button { @@ -41,14 +41,26 @@ } } -.bit-brc-sep { +.bit-brc-div { color: $clr-fg-sec; } +.bit-brc-trd { + transform: var(--bit-brc-div-transform); +} + +.bit-rtl { + --bit-brc-div-transform: scaleX(-1); +} + .bit-brc-obt { + display: flex; cursor: pointer; - line-height: spacing(4.5); color: $clr-fg-sec; + align-items: center; + justify-content: center; + line-height: spacing(4.5); + padding: 0px spacing(1.25); @media (hover: hover) { &:hover { @@ -57,13 +69,19 @@ } } -.bit-brc-itm { - cursor: pointer; +.bit-brc-itm, +.bit-brc-nii { + display: flex; + gap: spacing(1); font-weight: 400; + color: $clr-fg-pri; padding: 0 spacing(1); font-size: spacing(2.5); line-height: spacing(4.5); - color: $clr-fg-pri; +} + +.bit-brc-itm { + cursor: pointer; @media (hover: hover) { &:hover { @@ -72,11 +90,9 @@ } } -.bit-brc-oic { - display: flex; - align-items: center; - justify-content: center; - padding: 0px spacing(0.5); +.bit-brc-rvi { + flex-direction: row-reverse; + justify-content: space-between; } .bit-brc-cal { @@ -89,9 +105,9 @@ font-size: spacing(1.75); animation-fill-mode: both; animation-duration: 0.367s; + background-color: $clr-bg-pri; box-shadow: $box-shadow-callout; border-radius: $shp-border-radius; - background-color: $clr-bg-pri; animation-name: bit-fade-show, bit-slide-down; animation-timing-function: cubic-bezier(0.1, 0.9, 0.2, 1); @@ -106,14 +122,16 @@ max-height: spacing(88.875); } -.bit-brc-oitm { +.bit-brc-ofi, +.bit-brc-ofn { width: 100%; display: flex; - cursor: pointer; + gap: spacing(1); overflow: hidden; text-align: left; font-weight: 400; user-select: none; + color: $clr-fg-pri; position: relative; align-items: center; white-space: nowrap; @@ -127,8 +145,11 @@ min-width: spacing(22.5); line-height: spacing(2.5); background-color: transparent; - color: $clr-fg-pri; border: $shp-border-width $shp-border-style transparent; +} + +.bit-brc-ofi { + cursor: pointer; @media (hover: hover) { &:hover { @@ -144,15 +165,15 @@ height: 100%; display: none; position: fixed; - background-color: transparent; z-index: $zindex-overlay; + background-color: transparent; } -.bit-brc-disi { - pointer-events: none; +.bit-brc-dis { color: $clr-fg-dis; + pointer-events: none; } .bit-brc-sel { font-weight: 600; -} +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbClassStyles.cs new file mode 100644 index 0000000000..6f57f011f1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbClassStyles.cs @@ -0,0 +1,99 @@ +namespace Bit.BlazorUI; + +public class BitBreadcrumbClassStyles +{ + /// <summary> + /// Custom CSS classes/styles for the root element of the BitBreadcrumb. + /// </summary> + public string? Root { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the overlay of the BitBreadcrumb. + /// </summary> + public string? Overlay { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the item container of the BitBreadcrumb. + /// </summary> + public string? ItemContainer { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the overflow button of the BitBreadcrumb. + /// </summary> + public string? OverflowButton { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the overflow button icon of the BitBreadcrumb. + /// </summary> + public string? OverflowButtonIcon { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the item wrapper of the BitBreadcrumb. + /// </summary> + public string? ItemWrapper { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each item of the BitBreadcrumb. + /// </summary> + public string? Item { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each item icon of the BitBreadcrumb. + /// </summary> + public string? ItemIcon { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each item text of the BitBreadcrumb. + /// </summary> + public string? ItemText { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the selected item of the BitBreadcrumb. + /// </summary> + public string? SelectedItem { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the divider of the BitBreadcrumb. + /// </summary> + public string? Divider { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the divider icon of the BitBreadcrumb. + /// </summary> + public string? DividerIcon { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the callout element of the BitBreadcrumb. + /// </summary> + public string? Callout { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the callout container of the BitBreadcrumb. + /// </summary> + public string? CalloutContainer { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the overflow item wrapper of the BitBreadcrumb. + /// </summary> + public string? OverflowItemWrapper { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each overflow item of the BitBreadcrumb. + /// </summary> + public string? OverflowItem { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each overflow item icon of the BitBreadcrumb. + /// </summary> + public string? OverflowItemIcon { get; set; } + + /// <summary> + /// Custom CSS classes/styles for each overflow item text of the BitBreadcrumb. + /// </summary> + public string? OverflowItemText { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the overflow selected item of the BitBreadcrumb. + /// </summary> + public string? OverflowSelectedItem { get; set; } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbItem.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbItem.cs index 6f9cae0ef5..00dd24e4a9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbItem.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbItem.cs @@ -28,6 +28,16 @@ public class BitBreadcrumbItem /// </summary> public string? Style { get; set; } + /// <summary> + /// Name of an icon to render next to the item text. + /// </summary> + public string? IconName { get; set; } + + /// <summary> + /// Reverses the positions of the icon and the item text of the item content. + /// </summary> + public bool? ReversedIcon { get; set; } + /// <summary> /// Display the breadcrumb item as the selected item. /// </summary> @@ -42,4 +52,14 @@ public class BitBreadcrumbItem /// Click event handler of the breadcrumb item. /// </summary> public Action<BitBreadcrumbItem>? OnClick { get; set; } + + /// <summary> + /// The custom template for the item in overflow list. + /// </summary> + public RenderFragment<BitBreadcrumbItem>? OverflowTemplate { get; set; } + + /// <summary> + /// The custom template for the item. + /// </summary> + public RenderFragment<BitBreadcrumbItem>? Template { get; set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbNameSelectors.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbNameSelectors.cs index a13e5aa0e8..0f51209a55 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbNameSelectors.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbNameSelectors.cs @@ -27,6 +27,16 @@ public class BitBreadcrumbNameSelectors<TItem> where TItem : class /// </summary> public BitNameSelectorPair<TItem, string?> Style { get; set; } = new(nameof(BitBreadcrumbItem.Style)); + /// <summary> + /// The IconName field name and selector of the custom input class. + /// </summary> + public BitNameSelectorPair<TItem, string?> IconName { get; set; } = new(nameof(BitBreadcrumbItem.IconName)); + + /// <summary> + /// The ReversedIcon field name and selector of the custom input class. + /// </summary> + public BitNameSelectorPair<TItem, bool?> ReversedIcon { get; set; } = new(nameof(BitBreadcrumbItem.ReversedIcon)); + /// <summary> /// The IsSelected field name and selector of the custom input class. /// </summary> @@ -41,4 +51,14 @@ public class BitBreadcrumbNameSelectors<TItem> where TItem : class /// Click event handler of the item. /// </summary> public Action<TItem>? OnClick { get; set; } + + /// <summary> + /// The OverflowTemplate field name and selector of the custom input class. + /// </summary> + public BitNameSelectorPair<TItem, RenderFragment<TItem>?> OverflowTemplate { get; set; } = new(nameof(BitBreadcrumbItem.OverflowTemplate)); + + /// <summary> + /// The Template field name and selector of the custom input class. + /// </summary> + public BitNameSelectorPair<TItem, RenderFragment<TItem>?> Template { get; set; } = new(nameof(BitBreadcrumbItem.Template)); } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs similarity index 69% rename from src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor.cs rename to src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs index aabf84fe52..dc7ebe8e30 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs @@ -22,6 +22,16 @@ public partial class BitBreadcrumbOption : BitComponentBase, IDisposable /// </summary> [Parameter] public string? Href { get; set; } + /// <summary> + /// Name of an icon to render next to the item text. + /// </summary> + [Parameter] public string? IconName { get; set; } + + /// <summary> + /// Reverses the positions of the icon and the item text of the item content. + /// </summary> + [Parameter] public bool? ReversedIcon { get; set; } + /// <summary> /// Display the breadcrumb option as the selected option. /// </summary> @@ -32,6 +42,16 @@ public partial class BitBreadcrumbOption : BitComponentBase, IDisposable /// </summary> [Parameter] public EventCallback<BitBreadcrumbOption> OnClick { get; set; } + /// <summary> + /// The custom template for the option in overflow list. + /// </summary> + [Parameter] public RenderFragment<BitBreadcrumbOption>? OverflowTemplate { get; set; } + + /// <summary> + /// The custom template for the option. + /// </summary> + [Parameter] public RenderFragment<BitBreadcrumbOption>? Template { get; set; } + protected override string RootElementClass => "bit-bro"; protected override async Task OnInitializedAsync() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor deleted file mode 100644 index 5833ed7221..0000000000 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.razor +++ /dev/null @@ -1,2 +0,0 @@ -@namespace Bit.BlazorUI -@inherits BitComponentBase diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor index 98d58a3627..fcf1a1d9d7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor @@ -1,36 +1,40 @@ @namespace Bit.BlazorUI @inherits BitComponentBase -<button @ref="RootElement" @attributes="HtmlAttributes" - id="@_Id" - type="button" - aria-label="@AriaLabel" - @onclick="HandleOnClick" - style="@StyleBuilder.Value" - class="@ClassBuilder.Value" - dir="@Dir?.ToString().ToLower()" - tabindex="@(IsEnabled ? 0 : -1)"> - @if (Template is not null) - { - @Template - } - else - { - @if (IconName.HasValue()) +<div @ref="RootElement" @attributes="HtmlAttributes" + id="@_Id" + type="button" + aria-label="@AriaLabel" + style="@StyleBuilder.Value" + class="@ClassBuilder.Value" + dir="@Dir?.ToString().ToLower()" + tabindex="@(IsEnabled ? 0 : -1)"> + <button @onclick="HandleOnClick" + type="button" + style="@Styles?.Button" + class="bit-drm-btn @Classes?.Button"> + @if (Template is not null) { - <i style="@Styles?.Icon" class="bit-drm-icn bit-icon bit-icon--@IconName @Classes?.Icon" /> + @Template } - - @if (Text.HasValue()) + else { - <div style="@Styles?.Text" class="bit-drm-txt @Classes?.Text"> - @Text - </div> - } + @if (IconName.HasValue()) + { + <i style="@Styles?.Icon" class="bit-drm-icn bit-icon bit-icon--@IconName @Classes?.Icon" /> + } - <i style="@Styles?.ChevronDown" - class="bit-drm-cdi bit-icon bit-icon--@(ChevronDownIcon ?? "ChevronDown") @Classes?.ChevronDown" /> - } + @if (Text.HasValue()) + { + <div style="@Styles?.Text" class="bit-drm-txt @Classes?.Text"> + @Text + </div> + } + + <i style="@Styles?.ChevronDown" + class="bit-icon bit-icon--@(ChevronDownIcon ?? "ChevronRight bit-ico-r90") @Classes?.ChevronDown" /> + } + </button> <div @onclick="CloseCallout" @onclick:stopPropagation style="display:@(IsOpen ? "block" : "none");@Styles?.Overlay" @@ -41,4 +45,4 @@ class="bit-drm-cal @Classes?.Callout"> @(Body ?? ChildContent) </div> -</button> \ No newline at end of file +</div> \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor.cs index 38ec31b2aa..8075bf413b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.razor.cs @@ -40,8 +40,7 @@ public partial class BitDropMenu : BitComponentBase /// <summary> /// Determines the opening state of the callout of the drop menu. /// </summary> - [Parameter] - [CallOnSet(nameof(ToggleCallout))] + [Parameter, CallOnSet(nameof(ToggleCallout))] [ResetClassBuilder, ResetStyleBuilder, TwoWayBound] public bool IsOpen { get; set; } @@ -50,6 +49,16 @@ public partial class BitDropMenu : BitComponentBase /// </summary> [Parameter] public EventCallback OnClick { get; set; } + /// <summary> + /// The callback is called when the drop menu is dismissed. + /// </summary> + [Parameter] public EventCallback OnDismiss { get; set; } + + /// <summary> + /// Renders the drop menu in responsive mode on small screens. + /// </summary> + [Parameter] public bool Responsive { get; set; } + /// <summary> /// Custom CSS styles for different parts of the drop menu. /// </summary> @@ -72,6 +81,15 @@ public partial class BitDropMenu : BitComponentBase public bool Transparent { get; set; } + [JSInvokable("CloseCallout")] + public async Task __CloseCalloutBeforeAnotherCalloutIsOpened() + { + await DismissCallout(); + + StateHasChanged(); + } + + protected override string RootElementClass => "bit-drm"; @@ -101,6 +119,7 @@ protected override async Task OnInitializedAsync() } + private async Task HandleOnClick() { if (IsEnabled is false) return; @@ -119,7 +138,7 @@ private async Task OpenCallout() private async Task CloseCallout() { - if (await AssignIsOpen(false) is false) return; + await DismissCallout(); await ToggleCallout(); } @@ -134,7 +153,7 @@ await _js.ToggleCallout(_dotnetObj, _calloutId, null, IsOpen, - BitResponsiveMode.None, + Responsive ? BitResponsiveMode.Panel : BitResponsiveMode.None, BitDropDirection.TopAndBottom, Dir is BitDir.Rtl, "", @@ -144,4 +163,11 @@ await _js.ToggleCallout(_dotnetObj, false, RootElementClass); } + + private async Task DismissCallout() + { + if (await AssignIsOpen(false) is false) return; + + await OnDismiss.InvokeAsync(); + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss index 06e58cd927..9858b24068 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss @@ -1,21 +1,12 @@ @import "../../../Styles/functions.scss"; .bit-drm { - gap: spacing(1); - cursor: pointer; width: fit-content; - align-items: center; - display: inline-flex; - outline: transparent; - text-decoration: none; - box-sizing: border-box; - box-sizing: border-box; - justify-content: center; + color: $clr-fg-pri; font-family: $tg-font-family; font-weight: $tg-font-weight; background-color: $clr-bg-pri; border-radius: $shp-border-radius; - padding: spacing(1.0) spacing(1.5); @media (hover: hover) { &:hover { @@ -28,12 +19,39 @@ } &.bit-dis { - cursor: default; color: $clr-fg-dis; pointer-events: none; + + .bit-drm-btn { + cursor: default; + } + } + + &.bit-rtl { + .bit-drm-rsp { + animation-name: bit-fade-show, bit-drp-trans-x-reverse; + } } } +.bit-drm-btn { + color: inherit; + gap: spacing(1); + cursor: pointer; + width: fit-content; + align-items: center; + display: inline-flex; + outline: transparent; + font-weight: inherit; + font-family: inherit; + text-decoration: none; + box-sizing: border-box; + box-sizing: border-box; + justify-content: center; + background-color: transparent; + padding: spacing(1.0) spacing(1.5); +} + .bit-drm-trn { background-color: transparent; @@ -67,3 +85,10 @@ animation-name: bit-fade-show, bit-slide-down; animation-timing-function: cubic-bezier(0.1, 0.9, 0.2, 1); } + +.bit-drm-rsp { + height: 100%; + max-height: unset; + box-shadow: $box-shadow-callout; + animation-name: bit-fade-show, bit-drp-trans-x; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenuClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenuClassStyles.cs index f831b8bc12..157d63021e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenuClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenuClassStyles.cs @@ -12,6 +12,11 @@ public class BitDropMenuClassStyles /// </summary> public string? Opened { get; set; } + /// <summary> + /// Custom CSS classes/styles for the button of the BitDropMenu. + /// </summary> + public string? Button { get; set; } + /// <summary> /// Custom CSS classes/styles for the icon of the BitDropMenu. /// </summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor index 6d0ceaf684..b92b9bee92 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor @@ -10,11 +10,11 @@ <nav @ref="RootElement" @attributes="HtmlAttributes" id="@_Id" + role="navigation" + aria-label="@AriaLabel" style="@StyleBuilder.Value" class="@ClassBuilder.Value" - dir="@Dir?.ToString().ToLower()" - role="navigation" - aria-label="@AriaLabel"> + dir="@Dir?.ToString().ToLower()"> <ul role="list"> <CascadingValue Value="this" IsFixed="true"> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs index 9d6b476bc1..ac96fe05b4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs @@ -16,6 +16,12 @@ public partial class BitNav<TItem> : BitComponentBase, IDisposable where TItem : + /// <summary> + /// The accent color of the nav. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColor? Accent { get; set; } + /// <summary> /// The custom icon name of the chevron-down element of the BitNav component. /// </summary> @@ -31,11 +37,29 @@ public partial class BitNav<TItem> : BitComponentBase, IDisposable where TItem : /// </summary> [Parameter] public BitNavClassStyles? Classes { get; set; } + /// <summary> + /// The general color of the nav. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColor? Color { get; set; } + /// <summary> /// The initially selected item in manual mode. /// </summary> [Parameter] public TItem? DefaultSelectedItem { get; set; } + /// <summary> + /// Renders the nav in a width to only fit its content. + /// </summary> + [Parameter, ResetClassBuilder] + public bool FitWidth { get; set; } + + /// <summary> + /// Renders the nav in full width of its container element. + /// </summary> + [Parameter, ResetClassBuilder] + public bool FullWidth { get; set; } + /// <summary> /// Used to customize how content inside the group header is rendered. /// </summary> @@ -46,6 +70,12 @@ public partial class BitNav<TItem> : BitComponentBase, IDisposable where TItem : /// </summary> [Parameter] public BitNavItemTemplateRenderMode HeaderTemplateRenderMode { get; set; } = BitNavItemTemplateRenderMode.Normal; + /// <summary> + /// Only renders the icon of each nav item. + /// </summary> + [Parameter, ResetClassBuilder] + public bool IconOnly { get; set; } + /// <summary> /// The indentation value in px for each level of depth of child item. /// </summary> @@ -139,7 +169,9 @@ public partial class BitNav<TItem> : BitComponentBase, IDisposable where TItem : [Parameter] public BitNavClassStyles? Styles { get; set; } - + /// <summary> + /// Collapses all items and children. + /// </summary> public void CollapseAll() { foreach (var item in _items) @@ -148,6 +180,44 @@ public void CollapseAll() } } + /// <summary> + /// Toggles an item. + /// </summary> + public async Task ToggleItem(TItem Item) + { + var isExpanded = GetItemExpanded(Item) is false; + + if (SingleExpand) + { + if (isExpanded) + { + if (_currentItem is not null) + { + ToggleItemAndParents(_items, _currentItem, false); + } + } + + if (isExpanded) + { + ToggleItemAndParents(_items, Item, isExpanded); + } + else + { + SetItemExpanded(Item, isExpanded); + } + + StateHasChanged(); + + _currentItem = Item; + } + else + { + SetItemExpanded(Item, isExpanded); + } + + await OnItemToggle.InvokeAsync(Item); + } + internal BitNavAriaCurrent GetAriaCurrent(TItem item) @@ -676,42 +746,7 @@ internal string GetItemKey(TItem item) return GetKey(item) ?? Guid.NewGuid().ToString(); } - internal async Task ToggleItem(TItem Item) - { - var isExpanded = GetItemExpanded(Item) is false; - - if (SingleExpand) - { - if (isExpanded) - { - if (_currentItem is not null) - { - ToggleItemAndParents(_items, _currentItem, false); - } - } - - if (isExpanded) - { - ToggleItemAndParents(_items, Item, isExpanded); - } - else - { - SetItemExpanded(Item, isExpanded); - } - - StateHasChanged(); - - _currentItem = Item; - } - else - { - SetItemExpanded(Item, isExpanded); - } - - await OnItemToggle.InvokeAsync(Item); - } - - internal bool ToggleItemAndParents(IList<TItem> items, TItem item, bool isExpanded) + private bool ToggleItemAndParents(IList<TItem> items, TItem item, bool isExpanded) { foreach (var parent in items) { @@ -733,6 +768,55 @@ internal bool ToggleItemAndParents(IList<TItem> items, TItem item, bool isExpand protected override void RegisterCssClasses() { ClassBuilder.Register(() => Classes?.Root); + + ClassBuilder.Register(() => FitWidth ? "bit-nav-ftw" : string.Empty); + ClassBuilder.Register(() => FullWidth ? "bit-nav-flw" : string.Empty); + + ClassBuilder.Register(() => IconOnly ? "bit-nav-ion" : string.Empty); + + ClassBuilder.Register(() => Accent switch + { + BitColor.Primary => "bit-nav-apri", + BitColor.Secondary => "bit-nav-asec", + BitColor.Tertiary => "bit-nav-ater", + BitColor.Info => "bit-nav-ainf", + BitColor.Success => "bit-nav-asuc", + BitColor.Warning => "bit-nav-awrn", + BitColor.SevereWarning => "bit-nav-aswr", + BitColor.Error => "bit-nav-aerr", + BitColor.PrimaryBackground => "bit-nav-apbg", + BitColor.SecondaryBackground => "bit-nav-asbg", + BitColor.TertiaryBackground => "bit-nav-atbg", + BitColor.PrimaryForeground => "bit-nav-apfg", + BitColor.SecondaryForeground => "bit-nav-asfg", + BitColor.TertiaryForeground => "bit-nav-atfg", + BitColor.PrimaryBorder => "bit-nav-apbr", + BitColor.SecondaryBorder => "bit-nav-asbr", + BitColor.TertiaryBorder => "bit-nav-atbr", + _ => "bit-nav-apbg", + }); + + ClassBuilder.Register(() => Color switch + { + BitColor.Primary => "bit-nav-pri", + BitColor.Secondary => "bit-nav-sec", + BitColor.Tertiary => "bit-nav-ter", + BitColor.Info => "bit-nav-inf", + BitColor.Success => "bit-nav-suc", + BitColor.Warning => "bit-nav-wrn", + BitColor.SevereWarning => "bit-nav-swr", + BitColor.Error => "bit-nav-err", + BitColor.PrimaryBackground => "bit-nav-pbg", + BitColor.SecondaryBackground => "bit-nav-sbg", + BitColor.TertiaryBackground => "bit-nav-tbg", + BitColor.PrimaryForeground => "bit-nav-pfg", + BitColor.SecondaryForeground => "bit-nav-sfg", + BitColor.TertiaryForeground => "bit-nav-tfg", + BitColor.PrimaryBorder => "bit-nav-pbr", + BitColor.SecondaryBorder => "bit-nav-sbr", + BitColor.TertiaryBorder => "bit-nav-tbr", + _ => "bit-nav-pri", + }); } protected override void RegisterCssStyles() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.scss index 23d030bf41..15cd244688 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.scss @@ -1,6 +1,7 @@ @import "../../../Styles/functions.scss"; .bit-nav { + color: $clr-fg-pri; font-family: $tg-font-family; ul { @@ -18,20 +19,29 @@ pointer-events: none; background-color: transparent; - .bit-nav-itm { + .bit-nav-itm, + .bit-nav-iic { color: $clr-fg-dis; } } } +.bit-nav-ftw { + width: fit-content; +} + +.bit-nav-flw { + width: 100%; +} + .bit-nav-gcb, .bit-nav-cbt { cursor: pointer; user-select: none; - width: spacing(3.75); + color: $clr-fg-pri; font-size: spacing(1.5); + min-width: spacing(3.375); background-color: transparent; - color: $clr-fg-pri; i { transition: transform 0.1s linear 0s; @@ -39,7 +49,7 @@ } .bit-nav-exp { - transform: rotate(-180deg); + transform: rotate(-90deg); } .bit-nav-gcb { @@ -74,35 +84,27 @@ align-items: center; } -.bit-nav-des { - all: unset; - display: block; - cursor: pointer; - overflow: hidden; - white-space: nowrap; - font-weight: normal; - font-size: spacing(1.5); - padding: spacing(0.5) 0; - background: transparent; - text-overflow: ellipsis; - color: $clr-fg-dis; - padding-inline-start: spacing(2.75); -} - .bit-nav-ict { + all: unset; width: 100%; display: flex; + cursor: pointer; flex-flow: column; + gap: spacing(0.25); + padding: spacing(0.5); box-sizing: border-box; - min-height: spacing(5.5); + min-height: spacing(6.0); + border-color: transparent; border-radius: $shp-border-radius; + border-inline-start-width: spacing(0.375); + border-inline-start-style: $shp-border-style; @media (hover: hover) { &:hover { - background-color: $clr-bg-pri-hover; + background-color: var(--bit-nav-clr-hover); .bit-nav-itm { - color: $clr-pri; + color: var(--bit-nav-clr-text); } } } @@ -116,17 +118,31 @@ .bit-nav-itm { width: 100%; display: flex; + gap: spacing(1); cursor: pointer; overflow: hidden; font-weight: 400; user-select: none; - gap: spacing(0.5); + color: $clr-fg-pri; align-items: center; white-space: nowrap; flex-flow: row nowrap; text-overflow: ellipsis; font-size: spacing(1.75); - color: $clr-fg-sec; +} + +.bit-nav-des { + all: unset; + display: block; + cursor: pointer; + overflow: hidden; + color: $clr-fg-ter; + white-space: nowrap; + font-weight: normal; + font-size: spacing(1.5); + background: transparent; + text-overflow: ellipsis; + padding-inline-start: spacing(2.75); } .bit-nav-itm, @@ -142,19 +158,38 @@ } .bit-nav-iic { - color: $clr-pri; + color: var(--bit-nav-clr); +} + +.bit-nav-ion { + .bit-nav-ict { + padding: 0; + width: fit-content; + } + + .bit-nav-itm { + padding: 1rem; + font-size: 1rem; + } + + .bit-nav-itx { + display: none; + } + + .bit-nav-sel { + //border: none; + } } .bit-nav-sel { - background-color: $clr-bg-sec; - border-inline-start: spacing(0.375) $shp-border-style $clr-pri; + border-color: var(--bit-nav-clr); + background-color: var(--bit-nav-clr-active); - @media(hover:hover) { + @media (hover:hover) { &:hover { - background-color: $clr-bg-pri-hover; - /*.bit-nav-itm { - color: $clr-pri; - }*/ + .bit-nav-itm { + color: var(--bit-nav-clr-text); + } } } @@ -167,9 +202,10 @@ .bit-nav-dis { pointer-events: none; - .bit-nav-itm { - pointer-events: none; + .bit-nav-itm, + .bit-nav-iic { color: $clr-fg-dis; + pointer-events: none; } } @@ -177,3 +213,175 @@ height: 0; border: 1px solid $clr-brd-sec; } + + +.bit-nav-apri { + --bit-nav-clr-text: #{$clr-pri-text}; + --bit-nav-clr-hover: #{$clr-pri-hover}; + --bit-nav-clr-active: #{$clr-pri-active}; +} + +.bit-nav-asec { + --bit-nav-clr-text: #{$clr-sec-text}; + --bit-nav-clr-hover: #{$clr-sec-hover}; + --bit-nav-clr-active: #{$clr-sec-active}; +} + +.bit-nav-ater { + --bit-nav-clr-text: #{$clr-ter-text}; + --bit-nav-clr-hover: #{$clr-ter-hover}; + --bit-nav-clr-active: #{$clr-ter-active}; +} + +.bit-nav-ainf { + --bit-nav-clr-text: #{$clr-inf-text}; + --bit-nav-clr-hover: #{$clr-inf-hover}; + --bit-nav-clr-active: #{$clr-inf-active}; +} + +.bit-nav-asuc { + --bit-nav-clr-text: #{$clr-suc-text}; + --bit-nav-clr-hover: #{$clr-suc-hover}; + --bit-nav-clr-active: #{$clr-suc-active}; +} + +.bit-nav-awrn { + --bit-nav-clr-text: #{$clr-wrn-text}; + --bit-nav-clr-hover: #{$clr-wrn-hover}; + --bit-nav-clr-active: #{$clr-wrn-active}; +} + +.bit-nav-aswr { + --bit-nav-clr-text: #{$clr-swr-text}; + --bit-nav-clr-hover: #{$clr-swr-hover}; + --bit-nav-clr-active: #{$clr-swr-active}; +} + +.bit-nav-aerr { + --bit-nav-clr-text: #{$clr-err-text}; + --bit-nav-clr-hover: #{$clr-err-hover}; + --bit-nav-clr-active: #{$clr-err-active}; +} + +.bit-nav-apbg { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-bg-pri-hover}; + --bit-nav-clr-active: #{$clr-bg-pri-active}; +} + +.bit-nav-asbg { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-bg-sec-hover}; + --bit-nav-clr-active: #{$clr-bg-sec-active}; +} + +.bit-nav-atbg { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-bg-ter-hover}; + --bit-nav-clr-active: #{$clr-bg-ter-active}; +} + +.bit-nav-apfg { + --bit-nav-clr-text: #{$clr-bg-pri}; + --bit-nav-clr-hover: #{$clr-fg-pri-hover}; + --bit-nav-clr-active: #{$clr-fg-pri-active}; +} + +.bit-nav-asfg { + --bit-nav-clr-text: #{$clr-bg-pri}; + --bit-nav-clr-hover: #{$clr-fg-sec-hover}; + --bit-nav-clr-active: #{$clr-fg-sec-active}; +} + +.bit-nav-atfg { + --bit-nav-clr-text: #{$clr-bg-pri}; + --bit-nav-clr-hover: #{$clr-fg-ter-hover}; + --bit-nav-clr-active: #{$clr-fg-ter-active}; +} + +.bit-nav-apbr { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-brd-pri-hover}; + --bit-nav-clr-active: #{$clr-brd-pri-active}; +} + +.bit-nav-asbr { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-brd-sec-hover}; + --bit-nav-clr-active: #{$clr-brd-sec-active}; +} + +.bit-nav-atbr { + --bit-nav-clr-text: #{$clr-fg-pri}; + --bit-nav-clr-hover: #{$clr-brd-ter-hover}; + --bit-nav-clr-active: #{$clr-brd-ter-active}; +} + + +.bit-nav-pri { + --bit-nav-clr: #{$clr-pri}; +} + +.bit-nav-sec { + --bit-nav-clr: #{$clr-sec}; +} + +.bit-nav-ter { + --bit-nav-clr: #{$clr-ter}; +} + +.bit-nav-inf { + --bit-nav-clr: #{$clr-inf}; +} + +.bit-nav-suc { + --bit-nav-clr: #{$clr-suc}; +} + +.bit-nav-wrn { + --bit-nav-clr: #{$clr-wrn}; +} + +.bit-nav-swr { + --bit-nav-clr: #{$clr-swr}; +} + +.bit-nav-err { + --bit-nav-clr: #{$clr-err}; +} + +.bit-nav-pbg { + --bit-nav-clr: #{$clr-bg-pri}; +} + +.bit-nav-sbg { + --bit-nav-clr: #{$clr-bg-sec}; +} + +.bit-nav-tbg { + --bit-nav-clr: #{$clr-bg-ter}; +} + +.bit-nav-pfg { + --bit-nav-clr: #{$clr-fg-pri}; +} + +.bit-nav-sfg { + --bit-nav-clr: #{$clr-fg-sec}; +} + +.bit-nav-tfg { + --bit-nav-clr: #{$clr-fg-ter}; +} + +.bit-nav-pbr { + --bit-nav-clr: #{$clr-brd-pri}; +} + +.bit-nav-sbr { + --bit-nav-clr: #{$clr-brd-sec}; +} + +.bit-nav-tbr { + --bit-nav-clr: #{$clr-brd-ter}; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavClassStyles.cs index 68dde14fef..06023f8476 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavClassStyles.cs @@ -32,6 +32,11 @@ public class BitNavClassStyles /// </summary> public string? ItemIcon { get; set; } + /// <summary> + /// Custom CSS classes/styles for the item text of the BitNav. + /// </summary> + public string? ItemText { get; set; } + /// <summary> /// Custom CSS classes/styles for the selected item container of the BitNav. /// </summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor index 71f3ef7cae..5d74330a8d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor @@ -19,6 +19,7 @@ var target = Nav.GetTarget(Item); var renderLink = url.HasValue() || Nav.GetForceAnchor(Item); var href = (isEnabled && (Nav.SelectedItem != Item || Nav.Reselectable)) ? url : null; + var itemKey = Nav.GetItemKey(Item); } @if (Nav.RenderType is BitNavRenderType.Grouped && Parent is null) { @@ -63,7 +64,7 @@ { <div class="bit-nav-itg"> <div class="bit-nav-iit" style="@(Nav.ReversedChevron ? "flex-flow:row-reverse" : null)"> - <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon.HasValue() ? Nav.ChevronDownIcon : "ChevronDown") @(isExpanded ? "bit-nav-exp" : "")" aria-hidden="true" /> + <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon ?? "ChevronRight") @(Nav.ChevronDownIcon.HasNoValue() && isExpanded ? "bit-nav-exp" : "bit-ico-r90")" aria-hidden="true" /> <span class="bit-nav-ghd">@text</span> </div> <div title="@description" @@ -76,7 +77,7 @@ else { <div class="bit-nav-iit" style="@(Nav.ReversedChevron ? "flex-flow:row-reverse" : null)"> - <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon.HasValue() ? Nav.ChevronDownIcon : "ChevronDown") @(isExpanded ? "bit-nav-exp" : "")" aria-hidden="true" /> + <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon ?? "ChevronRight") @(Nav.ChevronDownIcon.HasNoValue() && isExpanded ? "bit-nav-exp" : "bit-ico-r90")" aria-hidden="true" /> <span class="bit-nav-ghd">@text</span> </div> } @@ -99,131 +100,79 @@ } else { - var padding = childItems.Any() - ? (Depth * Nav.IndentValue + (Nav.ReversedChevron ? Nav.IndentReversedPadding : 0)) - : (Depth * Nav.IndentValue + (Nav.ReversedChevron ? Nav.IndentReversedPadding : Nav.IndentPadding)); + var padding = Nav.IconOnly ? 0 + : Depth * Nav.IndentValue + (Nav.ReversedChevron ? Nav.IndentReversedPadding : (childItems.Any() ? 0 : Nav.IndentPadding)); - <div name="@text" - class="bit-nav-ict @GetItemContainerClasses()" - style="padding-inline-start:@(padding)px; @GetItemContainerStyles()"> + var desPadding = Nav.ReversedChevron is false && childItems.Any() ? Nav.IndentPadding : 0; - <div class="bit-nav-mct" style="@(Nav.ReversedChevron ? "flex-flow:row-reverse" : null)"> - @if (childItems.Any()) - { - <button @onclick="ToggleItem" - type="button" - tabindex="0" - style="@Nav.Styles?.ToggleButton" - class="bit-nav-cbt @Nav.Classes?.ToggleButton" - aria-expanded="@(isExpanded ? "true" : "false")" - aria-label="@(isExpanded ? colAriaLabel : expAriaLabel)"> - <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon.HasValue() ? Nav.ChevronDownIcon : "ChevronDown") @(isExpanded ? "bit-nav-exp" : "")" aria-hidden="true" /> - </button> - } - @if (template is not null && templateRenderMode is BitNavItemTemplateRenderMode.Replace) - { - @template(Item) - } - else if (Nav.ItemTemplate is not null && Nav.ItemTemplateRenderMode is BitNavItemTemplateRenderMode.Replace) - { - @Nav.ItemTemplate(Item) - } - else - { - @if (renderLink) + @if (template is not null && templateRenderMode is BitNavItemTemplateRenderMode.Replace) + { + @template(Item) + } + else if (Nav.ItemTemplate is not null && Nav.ItemTemplateRenderMode is BitNavItemTemplateRenderMode.Replace) + { + @Nav.ItemTemplate(Item) + } + else + { + <_BitNavItemContainer Href="@href" + Target="@target" + OnClick="HandleOnClick" + RenderLink="renderLink" + Disabled="@(isEnabled is false)" + AriaLabel="@Nav.GetAriaLabel(Item)" + Title="@(title is not null ? title : text)" + Class="@($"bit-nav-ict {GetItemContainerClasses()}")" + Style="@($"padding-inline-start:{padding}px;{GetItemContainerStyles()}")" + AriaCurrent="@(Item == Nav.SelectedItem ? _AriaCurrentMap[Nav.GetAriaCurrent(Item)] : null)" + Rel="@(url.HasValue() && target.HasValue() && IsRelativeUrl(url!) is false ? "noopener noreferrer" : null)"> + <div class="bit-nav-mct" style="@(Nav.ReversedChevron ? "flex-flow:row-reverse" : null)"> + @if (childItems.Any() && Nav.IconOnly is false) { - <a @onclick="HandleOnClick" - tabindex="-1" - style="@GetItemStyles()" - target="@target" - aria-label="@Nav.GetAriaLabel(Item)" - class="bit-nav-itm @GetItemClasses()" - disabled="@(isEnabled is false)" - href="@href" - title="@(title is not null ? title : text)" - aria-current="@(Item == Nav.SelectedItem ? _AriaCurrentMap[Nav.GetAriaCurrent(Item)] : null)" - rel="@(url.HasValue() && target.HasValue() && IsRelativeUrl(url!) is false ? "noopener noreferrer" : null)"> - @if (template is not null) - { - @template(Item) - } - else if (Nav.ItemTemplate is not null) - { - @Nav.ItemTemplate(Item) - } - else - { - @if (iconName.HasValue()) - { - <i style="@Nav.Styles?.ItemIcon" class="bit-nav-iic bit-icon bit-icon--@iconName @Nav.Classes?.ItemIcon" aria-hidden="true" /> - } - <span>@text</span> - } - </a> + <div @onclick="ToggleItem" + @onclick:preventDefault="true" + @onclick:stopPropagation="true" + tabindex="0" + type="button" + style="@Nav.Styles?.ToggleButton" + class="bit-nav-cbt @Nav.Classes?.ToggleButton" + aria-expanded="@(isExpanded ? "true" : "false")" + aria-label="@(isExpanded ? colAriaLabel : expAriaLabel)"> + <i class="bit-icon bit-icon--@(Nav.ChevronDownIcon ?? "ChevronRight") @(Nav.ChevronDownIcon.HasNoValue() && isExpanded ? "bit-nav-exp" : "bit-ico-r90")" aria-hidden="true" /> + </div> } - else - { - <button @onclick="HandleOnClick" - type="button" - tabindex="-1" - style="@GetItemStyles()" - aria-label="@Nav.GetAriaLabel(Item)" - class="bit-nav-itm @GetItemClasses()" - disabled="@(isEnabled is false)" - title="@(title is not null ? title : text)" - aria-current="@(Item == Nav.SelectedItem ? _AriaCurrentMap[Nav.GetAriaCurrent(Item)] : null)"> - @if (template is not null) - { - @template(Item) - } - else if (Nav.ItemTemplate is not null) - { - @Nav.ItemTemplate(Item) - } - else + <div style="@GetItemStyles()" + class="bit-nav-itm @GetItemClasses()"> + @if (template is not null) + { + @template(Item) + } + else if (Nav.ItemTemplate is not null) + { + @Nav.ItemTemplate(Item) + } + else + { + @if (iconName.HasValue()) { - @if (iconName.HasValue()) - { - <i style="@Nav.Styles?.ItemIcon" class="bit-nav-iic bit-icon bit-icon--@iconName @Nav.Classes?.ItemIcon" aria-hidden="true" /> - } - <span>@text</span> + <i style="@Nav.Styles?.ItemIcon" class="bit-nav-iic bit-icon bit-icon--@iconName @Nav.Classes?.ItemIcon" aria-hidden="true" /> } - </button> - } - } - </div> + <span style="@Nav.Styles?.ItemText" class="bit-nav-itx @Nav.Classes?.ItemText">@text</span> + } + </div> + </div> - @if (description.HasValue()) - { - @if (renderLink) + @if (description.HasValue() && Nav.IconOnly is false) { - <a @onclick="HandleOnClick" - tabindex="-1" - target="@target" - title="@description" - disabled="@(isEnabled is false)" - href="@href" - style="padding-inline-start:@((Nav.ReversedChevron is false && childItems.Any()) ? 27 : 0)px; @Nav.Styles?.Description" - class="bit-nav-des @Nav.Classes?.Description" - rel="@(url.HasValue() && target.HasValue() && IsRelativeUrl(url!) is false ? "noopener noreferrer" : null)"> + <div title="@description" + class="bit-nav-des @Nav.Classes?.Description" + style="padding-inline-start:@(desPadding)px;@Nav.Styles?.Description"> @description - </a> - } - else - { - <button @onclick="HandleOnClick" - type="button" - tabindex="-1" - title="@description" - disabled="@(isEnabled is false)" - style="padding-inline-start:@((Nav.ReversedChevron is false && childItems.Any()) ? 27 : 0)px; @Nav.Styles?.Description" - class="bit-nav-des @Nav.Classes?.Description"> - @description - </button> + </div> } - } - </div> + </_BitNavItemContainer> + } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs index cd5958deec..71e43381aa 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs @@ -70,18 +70,24 @@ private string GetItemContainerClasses() } private string GetItemContainerStyles() { - var classes = new List<string>(); + var styles = new List<string>(); + + if (Nav.FitWidth && Nav.IconOnly is false) + { + styles.Add($"padding-inline-end:{Nav.IndentPadding}px;"); + } + if (Nav.Styles?.ItemContainer is not null) { - classes.Add(Nav.Styles.ItemContainer); + styles.Add(Nav.Styles.ItemContainer); } if (Nav.SelectedItem == Item && Nav.Styles?.SelectedItemContainer is not null) { - classes.Add(Nav.Styles?.SelectedItemContainer!); + styles.Add(Nav.Styles?.SelectedItemContainer!); } - return string.Join(" ", classes); + return string.Join(" ", styles); } private string GetItemClasses() { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor new file mode 100644 index 0000000000..3b35883309 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor @@ -0,0 +1,32 @@ +@namespace Bit.BlazorUI + +@if (RenderLink) +{ + <a @onclick="OnClick" + rel="@Rel" + href="@Href" + tabindex="-1" + title="@Title" + class="@Class" + style="@Style" + target="@Target" + disabled="@Disabled" + aria-label="@AriaLabel" + aria-current="@AriaCurrent"> + @ChildContent + </a> +} +else +{ + <button @onclick="OnClick" + type="button" + title="@Title" + tabindex="-1" + class="@Class" + style="@Style" + disabled="@Disabled" + aria-label="@AriaLabel" + aria-current="@AriaCurrent"> + @ChildContent + </button> +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor.cs new file mode 100644 index 0000000000..3d1811feef --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavItemContainer.razor.cs @@ -0,0 +1,29 @@ +namespace Bit.BlazorUI; + +public partial class _BitNavItemContainer +{ + [Parameter] public string? AriaCurrent { get; set; } + + [Parameter] public string? AriaLabel { get; set; } + + [Parameter] public RenderFragment? ChildContent { get; set; } + + [Parameter] public string? Class { get; set; } + + [Parameter] public bool Disabled { get; set; } + + [Parameter] public string? Href { get; set; } + + [Parameter] public EventCallback OnClick { get; set; } + + [Parameter] public string? Rel { get; set; } + + [Parameter] public bool RenderLink { get; set; } + + [Parameter] public string? Style { get; set; } + + [Parameter] public string? Target { get; set; } + + [Parameter] public string? Title { get; set; } + +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor index 9bc866b6d8..4c360b58c6 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor @@ -13,7 +13,7 @@ disabled="@(SelectedPage == 1 || IsEnabled is false)" style="@Styles?.Button @Styles?.FirstButton" class="bit-pgn-btn @Classes?.Button @Classes?.FirstButton @GetButtonClasses()"> - <i style="@Styles?.FirstButtonIcon" class="bit-pgn-ico bit-icon bit-icon--@FirstIcon @Styles?.FirstButtonIcon" /> + <i style="@Styles?.FirstButtonIcon" class="bit-icon bit-icon--@(FirstIcon ?? "ChevronRightEnd6 bit-pgn-trs") @Styles?.FirstButtonIcon" /> </button> } @@ -24,7 +24,7 @@ disabled="@(SelectedPage == 1 || IsEnabled is false)" style="@Styles?.Button @Styles?.PreviousButton" class="bit-pgn-btn @Classes?.Button @Classes?.PreviousButton @GetButtonClasses()"> - <i style="@Styles?.PreviousButtonIcon" class="bit-pgn-ico bit-icon bit-icon--@PreviousIcon @Classes?.PreviousButtonIcon" /> + <i style="@Styles?.PreviousButtonIcon" class="bit-icon bit-icon--@(PreviousIcon ?? "ChevronRight bit-pgn-trs") @Classes?.PreviousButtonIcon" /> </button> } @@ -64,7 +64,7 @@ disabled="@(SelectedPage == Count || IsEnabled is false)" style="@Styles?.Button @Styles?.NextButton" class="bit-pgn-btn @Classes?.Button @Classes?.NextButton @GetButtonClasses()"> - <i style="@Styles?.NextButtonIcon" class="bit-pgn-ico bit-icon bit-icon--@NextIcon @Classes?.NextButtonIcon" /> + <i style="@Styles?.NextButtonIcon" class="bit-icon bit-icon--@(NextIcon ?? "ChevronRight bit-pgn-tre") @Classes?.NextButtonIcon" /> </button> } @@ -75,7 +75,7 @@ disabled="@(SelectedPage == Count || IsEnabled is false)" style="@Styles?.Button @Styles?.LastButton" class="bit-pgn-btn @Classes?.Button @Classes?.LastButton @GetButtonClasses()"> - <i style="@Styles?.LastButtonIcon" class="bit-pgn-ico bit-icon bit-icon--@LastIcon @Classes?.LastButtonIcon" /> + <i style="@Styles?.LastButtonIcon" class="bit-icon bit-icon--@(LastIcon ?? "ChevronRightEnd6 bit-pgn-tre") @Classes?.LastButtonIcon" /> </button> } </div> \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor.cs index cee824e8de..2be858bb4d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.razor.cs @@ -43,12 +43,12 @@ public partial class BitPagination : BitComponentBase /// <summary> /// The icon name of the first button. /// </summary> - [Parameter] public string FirstIcon { get; set; } = "ChevronLeftEnd6"; + [Parameter] public string? FirstIcon { get; set; } /// <summary> /// The icon name of the last button. /// </summary> - [Parameter] public string LastIcon { get; set; } = "ChevronRightEnd6"; + [Parameter] public string? LastIcon { get; set; } /// <summary> /// The number of items to render in the middle of the pagination. @@ -60,7 +60,7 @@ public partial class BitPagination : BitComponentBase /// <summary> /// The icon name of the next button. /// </summary> - [Parameter] public string NextIcon { get; set; } = "ChevronRight"; + [Parameter] public string? NextIcon { get; set; } /// <summary> /// The event callback for when selected page changes. @@ -70,7 +70,7 @@ public partial class BitPagination : BitComponentBase /// <summary> /// The icon name of the previous button. /// </summary> - [Parameter] public string PreviousIcon { get; set; } = "ChevronLeft"; + [Parameter] public string? PreviousIcon { get; set; } /// <summary> /// The selected page number. diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss index c737ef1062..45aaba6ff9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss @@ -6,6 +6,8 @@ align-items: center; display: inline-flex; font-size: var(--bit-pgn-font-size); + --bit-pgn-ico-transform-end: scaleX(1); + --bit-pgn-ico-transform-start: scaleX(-1); --bit-pgn-clr-dis: #{$clr-bg-dis}; --bit-pgn-clr-txt-dis: #{$clr-fg-dis}; --bit-pgn-clr-btn-txt-hover: var(--bit-pgn-clr-txt); @@ -32,12 +34,17 @@ } } -.bit-pgn-ico { - transform: var(--bit-pgn-ico-transform); +.bit-pgn-trs { + transform: var(--bit-pgn-ico-transform-start); +} + +.bit-pgn-tre { + transform: var(--bit-pgn-ico-transform-end); } .bit-rtl { - --bit-pgn-ico-transform: scale(-1); + --bit-pgn-ico-transform-end: scaleX(-1); + --bit-pgn-ico-transform-start: scaleX(1); } .bit-pgn-elp { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor index 9e446fdd74..bea89e9690 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor @@ -15,7 +15,7 @@ </div> </CascadingValue> - @if (HeadersOnly is false) + @if (HeaderOnly is false) { <div role="tabpanel" aria-hidden="false" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor.cs index 31430f964a..4f2b78e2d4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.razor.cs @@ -14,63 +14,69 @@ public partial class BitPivot : BitComponentBase public BitAlignment? Alignment { get; set; } /// <summary> - /// The content of pivot, It can be Any custom tag + /// The content of pivot. /// </summary> [Parameter] public RenderFragment? ChildContent { get; set; } /// <summary> - /// Custom CSS classes for different parts of the BitPivot component. + /// Custom CSS classes for different parts of the pivot. /// </summary> [Parameter] public BitPivotClassStyles? Classes { get; set; } /// <summary> - /// Default selected key for the pivot + /// Default selected key for the pivot. /// </summary> [Parameter] public string? DefaultSelectedKey { get; set; } /// <summary> - /// Whether to skip rendering the tabpanel with the content of the selected tab + /// Whether to skip rendering the tabpanel with the content of the selected tab. /// </summary> - [Parameter] public bool HeadersOnly { get; set; } = false; + [Parameter] public bool HeaderOnly { get; set; } = false; /// <summary> - /// Pivot link format, display mode for the pivot links + /// The type of the pivot header items. /// </summary> [Parameter, ResetClassBuilder] - public BitPivotLinkFormat LinkFormat { get; set; } = BitPivotLinkFormat.Links; + public BitPivotHeaderType? HeaderType { get; set; } /// <summary> - /// The size of the pivot links. + /// Callback for when a pivot header item is clicked. /// </summary> - [Parameter, ResetClassBuilder] - public BitSize? LinkSize { get; set; } + [Parameter] public EventCallback<BitPivotItem> OnItemClick { get; set; } /// <summary> - /// Overflow behavior when there is not enough room to display all of the links/tabs + /// Callback for when the selected pivot item changes. /// </summary> - [Parameter, ResetClassBuilder] - public BitPivotOverflowBehavior OverflowBehavior { get; set; } + [Parameter] + public EventCallback<BitPivotItem> OnChange { get; set; } /// <summary> - /// Callback for when the a pivot item is clicked. + /// Overflow behavior when there is not enough room to display all of the links/tabs. /// </summary> - [Parameter] public EventCallback<BitPivotItem> OnItemClick { get; set; } + [Parameter, ResetClassBuilder] + public BitPivotOverflowBehavior? OverflowBehavior { get; set; } /// <summary> - /// Position of the pivot header + /// Position of the pivot header. /// </summary> [Parameter, ResetClassBuilder] - public BitPivotPosition Position { get; set; } + public BitPivotPosition? Position { get; set; } /// <summary> - /// Key of the selected pivot item. Updating this will override the Pivot's selected item state + /// Key of the selected pivot item. /// </summary> [Parameter, TwoWayBound] [CallOnSet(nameof(OnSetSelectedKey))] public string? SelectedKey { get; set; } /// <summary> - /// Custom CSS styles for different parts of the BitPivot component. + /// The size of the pivot header items. + /// </summary> + [Parameter, ResetClassBuilder] + public BitSize? Size { get; set; } + + /// <summary> + /// Custom CSS styles for different parts of the pivot. /// </summary> [Parameter] public BitPivotClassStyles? Styles { get; set; } @@ -82,30 +88,36 @@ protected override void RegisterCssClasses() { ClassBuilder.Register(() => Classes?.Root); - ClassBuilder.Register(() => LinkSize switch + ClassBuilder.Register(() => Size switch { BitSize.Small => "bit-pvt-sm", BitSize.Medium => "bit-pvt-md", BitSize.Large => "bit-pvt-lg", - _ => string.Empty - }).Register(() => LinkFormat switch + _ => "bit-pvt-md" + }); + + ClassBuilder.Register(() => HeaderType switch { - BitPivotLinkFormat.Links => "bit-pvt-links", - BitPivotLinkFormat.Tabs => "bit-pvt-tabs", - _ => string.Empty - }).Register(() => OverflowBehavior switch + BitPivotHeaderType.Link => "bit-pvt-lnk", + BitPivotHeaderType.Tab => "bit-pvt-tab", + _ => "bit-pvt-lnk" + }); + + ClassBuilder.Register(() => OverflowBehavior switch { - BitPivotOverflowBehavior.Menu => "bit-pvt-menu", - BitPivotOverflowBehavior.Scroll => "bit-pvt-scroll", - BitPivotOverflowBehavior.None => "bit-pvt-none", - _ => string.Empty - }).Register(() => Position switch + BitPivotOverflowBehavior.Menu => "bit-pvt-mnu", + BitPivotOverflowBehavior.Scroll => "bit-pvt-scr", + BitPivotOverflowBehavior.None => "bit-pvt-non", + _ => "bit-pvt-non" + }); + + ClassBuilder.Register(() => Position switch { BitPivotPosition.Top => "bit-pvt-top", - BitPivotPosition.Bottom => "bit-pvt-bottom", - BitPivotPosition.Left => "bit-pvt-left", - BitPivotPosition.Right => "bit-pvt-right", - _ => string.Empty + BitPivotPosition.Bottom => "bit-pvt-btm", + BitPivotPosition.Left => "bit-pvt-lft", + BitPivotPosition.Right => "bit-pvt-rgt", + _ => "bit-pvt-top" }); } @@ -141,7 +153,7 @@ protected override async Task OnInitializedAsync() internal int GetPivotItemTabIndex(BitPivotItem item) => item.IsSelected ? 0 : _allItems.FindIndex(i => i == item) == 0 ? 0 : -1; - internal void SelectItem(BitPivotItem item) + internal async void SelectItem(BitPivotItem item) { if (SelectedKeyHasBeenSet && SelectedKeyChanged.HasDelegate is false) return; @@ -151,6 +163,8 @@ internal void SelectItem(BitPivotItem item) _selectedItem = item; _ = AssignSelectedKey(item.Key); + await OnChange.InvokeAsync(item); + StateHasChanged(); } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss index b56e20fe53..18f1fa6f7d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss @@ -1,6 +1,7 @@ @import "../../../Styles/functions.scss"; .bit-pvt { + gap: 1rem; display: flex; &.bit-dis { @@ -48,15 +49,15 @@ flex-flow: column; } -.bit-pvt-bottom { +.bit-pvt-btm { flex-flow: column-reverse; } -.bit-pvt-left { +.bit-pvt-lft { flex-flow: row; } -.bit-pvt-right { +.bit-pvt-rgt { flex-flow: row-reverse; } @@ -68,7 +69,7 @@ } } -.bit-pvt-links { +.bit-pvt-lnk { .bit-pvti { @media(hover:hover) { &:hover { @@ -95,7 +96,7 @@ } } -.bit-pvt-tabs { +.bit-pvt-tab { .bit-pvti { @media(hover:hover) { &:hover { @@ -112,7 +113,7 @@ } } -.bit-pvt-scroll { +.bit-pvt-scr { .bit-pvt-hct { overflow-x: auto; overflow-y: hidden; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotHeaderType.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotHeaderType.cs new file mode 100644 index 0000000000..2b2644d1f3 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotHeaderType.cs @@ -0,0 +1,14 @@ +namespace Bit.BlazorUI; + +public enum BitPivotHeaderType +{ + /// <summary> + /// Renders pivot header items as Tab. + /// </summary> + Tab, + + /// <summary> + /// Renders pivot header items as link. + /// </summary> + Link +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotLinkFormat.cs b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotLinkFormat.cs deleted file mode 100644 index 9d8381e854..0000000000 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivotLinkFormat.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Bit.BlazorUI; - -public enum BitPivotLinkFormat -{ - /// <summary> - /// Display Pivot Links as Tabs - /// </summary> - Tabs, - - /// <summary> - /// Display Pivot Links as links - /// </summary> - Links -} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.razor.cs index 5d21d4967b..83c04214f5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.razor.cs @@ -1,6 +1,4 @@ -using System.Text; - -namespace Bit.BlazorUI; +namespace Bit.BlazorUI; public partial class BitBadge : BitComponentBase { @@ -27,7 +25,9 @@ public partial class BitBadge : BitComponentBase /// <summary> /// Content you want inside the badge. Supported types are string and integer. /// </summary> - [Parameter] public object? Content { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetContentAndMax))] + public object? Content { get; set; } /// <summary> /// Reduces the size of the badge and hide any of its content. @@ -48,7 +48,9 @@ public partial class BitBadge : BitComponentBase /// <summary> /// Max value to display when content is integer type. /// </summary> - [Parameter] public int? Max { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetContentAndMax))] + public int? Max { get; set; } /// <summary> /// Button click event if set. @@ -145,7 +147,16 @@ protected override void RegisterCssStyles() StyleBuilder.Register(() => Styles?.Root); } - protected override void OnParametersSet() + + + private async Task HandleOnClick(MouseEventArgs e) + { + if (IsEnabled is false) return; + + await OnClick.InvokeAsync(e); + } + + private void OnSetContentAndMax() { if (Content is string stringContent) { @@ -167,13 +178,4 @@ protected override void OnParametersSet() _content = null; } } - - - - private async Task HandleOnClick(MouseEventArgs e) - { - if (IsEnabled is false) return; - - await OnClick.InvokeAsync(e); - } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor index 42b564c6e1..53beeaa963 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor @@ -6,18 +6,20 @@ style="@StyleBuilder.Value" class="@ClassBuilder.Value" dir="@Dir?.ToString().ToLower()"> - <div style="@Styles?.Container" class="bit-msg-con @Classes?.Container"> - @if (HideIcon is false) - { - <div style="@Styles?.IconContainer" class="bit-msg-ict @Classes?.IconContainer" aria-hidden="true"> - <i style="@Styles?.Icon" class="bit-msg-ico bit-icon bit-icon--@GetIconName() @Classes?.Icon" /> - </div> - } + <div style="@Styles?.RootContainer" class="bit-msg-rct @Classes?.RootContainer"> + <div style="@Styles?.Container" class="bit-msg-con @Classes?.Container"> + @if (HideIcon is false) + { + <div style="@Styles?.IconContainer" class="bit-msg-ict @Classes?.IconContainer" aria-hidden="true"> + <i style="@Styles?.Icon" class="bit-msg-ico bit-icon bit-icon--@GetIconName() @Classes?.Icon" /> + </div> + } - <div style="@Styles?.ContentContainer" class="bit-msg-cnc@(_isExpanded ? " bit-msg-cnx": "") @Classes?.ContentContainer" role="@GetTextRole()"> - <div style="@Styles?.ContentWrapper" class="bit-msg-cnw @Classes?.ContentWrapper"> - <div style="@Styles?.Content" class="bit-msg-cnt@(Multiline ? " bit-msg-mcn" : "") @Classes?.Content"> - @(Content ?? ChildContent) + <div style="@Styles?.ContentContainer" class="bit-msg-cnc@(_isExpanded ? " bit-msg-cnx": "") @Classes?.ContentContainer" role="@GetTextRole()"> + <div style="@Styles?.ContentWrapper" class="bit-msg-cnw @Classes?.ContentWrapper"> + <div style="@Styles?.Content" class="bit-msg-cnt@(Multiline ? " bit-msg-mcn" : "") @Classes?.Content"> + @(Content ?? ChildContent) + </div> </div> </div> </div> @@ -30,14 +32,14 @@ @if (Truncate && Multiline is false) { <button style="@Styles?.ExpanderButton" class="bit-msg-exb @Classes?.ExpanderButton" @onclick="ToggleExpand" type="button"> - <i style="@Styles?.ExpanderIcon" class="bit-msg-exi bit-icon bit-icon--@(_isExpanded ? CollapseIconName : ExpandIconName) @Classes?.ExpanderIcon" aria-hidden="true" /> + <i style="@Styles?.ExpanderIcon" class="bit-msg-exi bit-icon bit-icon--@(_isExpanded ? CollapseIcon : ExpandIcon) @Classes?.ExpanderIcon" aria-hidden="true" /> </button> } @if (OnDismiss.HasDelegate) { <button style="@Styles?.DismissButton" class="bit-msg-dmb @Classes?.DismissButton" @onclick="OnDismiss" type="button"> - <i style="@Styles?.DismissIcon" class="bit-msg-dmi bit-icon bit-icon--@DismissIconName @Classes?.DismissIcon" aria-hidden="true" /> + <i style="@Styles?.DismissIcon" class="bit-msg-dmi bit-icon bit-icon--@DismissIcon @Classes?.DismissIcon" aria-hidden="true" /> </button> } </div> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs index a39ca5909d..6b3e455d6b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs @@ -11,6 +11,12 @@ public partial class BitMessage : BitComponentBase /// </summary> [Parameter] public RenderFragment? Actions { get; set; } + /// <summary> + /// Determines the alignment of the content section of the message. + /// </summary> + [Parameter, ResetStyleBuilder] + public BitAlignment? Alignment { get; set; } + /// <summary> /// The content of message. /// </summary> @@ -24,7 +30,7 @@ public partial class BitMessage : BitComponentBase /// <summary> /// Custom Fabric icon name for the collapse icon in Truncate mode. If unset, default will be the Fabric DoubleChevronUp icon. /// </summary> - [Parameter] public string CollapseIconName { get; set; } = "DoubleChevronUp"; + [Parameter] public string CollapseIcon { get; set; } = "DoubleChevronUp"; /// <summary> /// The general color of the message. @@ -40,12 +46,18 @@ public partial class BitMessage : BitComponentBase /// <summary> /// Custom Fabric icon name to replace the dismiss icon. If unset, default will be the Fabric Cancel icon. /// </summary> - [Parameter] public string DismissIconName { get; set; } = "Cancel"; + [Parameter] public string DismissIcon { get; set; } = "Cancel"; + + /// <summary> + /// Determines the elevation of the message, a scale from 1 to 24. + /// </summary> + [Parameter, ResetStyleBuilder] + public int? Elevation { get; set; } /// <summary> /// Custom Fabric icon name for the expand icon in Truncate mode. If unset, default will be the Fabric DoubleChevronDown icon. /// </summary> - [Parameter] public string ExpandIconName { get; set; } = "DoubleChevronDown"; + [Parameter] public string ExpandIcon { get; set; } = "DoubleChevronDown"; /// <summary> /// Prevents rendering the icon of the message. @@ -72,6 +84,12 @@ public partial class BitMessage : BitComponentBase /// </summary> [Parameter] public string? Role { get; set; } + /// <summary> + /// The size of Message, Possible values: Small | Medium | Large + /// </summary> + [Parameter, ResetClassBuilder] + public BitSize? Size { get; set; } + /// <summary> /// Custom CSS styles for different parts of the BitMessage. /// </summary> @@ -97,6 +115,21 @@ public partial class BitMessage : BitComponentBase protected override void RegisterCssStyles() { StyleBuilder.Register(() => Styles?.Root); + + StyleBuilder.Register(() => Alignment switch + { + BitAlignment.Start => "--bit-msg-justifycontent:flex-start", + BitAlignment.End => "--bit-msg-justifycontent:flex-end", + BitAlignment.Center => "--bit-msg-justifycontent:center", + BitAlignment.SpaceBetween => "--bit-msg-justifycontent:space-between", + BitAlignment.SpaceAround => "--bit-msg-justifycontent:space-around", + BitAlignment.SpaceEvenly => "--bit-msg-justifycontent:space-evenly", + BitAlignment.Baseline => "--bit-msg-justifycontent:baseline", + BitAlignment.Stretch => "--bit-msg-justifycontent:stretch", + _ => "--bit-msg-justifycontent:flex-start" + }); + + StyleBuilder.Register(() => Elevation is > 0 or < 25 ? $"--bit-msg-boxshadow:var(--bit-shd-{Elevation.Value})" : string.Empty); } protected override void RegisterCssClasses() @@ -121,8 +154,25 @@ protected override void RegisterCssClasses() BitColor.Warning => "bit-msg-wrn", BitColor.SevereWarning => "bit-msg-swr", BitColor.Error => "bit-msg-err", + BitColor.PrimaryBackground => "bit-msg-pbg", + BitColor.SecondaryBackground => "bit-msg-sbg", + BitColor.TertiaryBackground => "bit-msg-tbg", + BitColor.PrimaryForeground => "bit-msg-pfg", + BitColor.SecondaryForeground => "bit-msg-sfg", + BitColor.TertiaryForeground => "bit-msg-tfg", + BitColor.PrimaryBorder => "bit-msg-pbr", + BitColor.SecondaryBorder => "bit-msg-sbr", + BitColor.TertiaryBorder => "bit-msg-tbr", _ => "bit-msg-inf" }); + + ClassBuilder.Register(() => Size switch + { + BitSize.Small => "bit-msg-sm", + BitSize.Medium => "bit-msg-md", + BitSize.Large => "bit-msg-lg", + _ => "bit-msg-md" + }); } private void ToggleExpand() => _isExpanded = _isExpanded is false; @@ -140,6 +190,15 @@ protected override void RegisterCssClasses() [BitColor.Success] = "Completed", [BitColor.Warning] = "Info", [BitColor.SevereWarning] = "Warning", - [BitColor.Error] = "ErrorBadge" + [BitColor.Error] = "ErrorBadge", + [BitColor.PrimaryBackground] = "Info", + [BitColor.SecondaryBackground] = "Info", + [BitColor.TertiaryBackground] = "Info", + [BitColor.PrimaryForeground] = "Info", + [BitColor.SecondaryForeground] = "Info", + [BitColor.TertiaryForeground] = "Info", + [BitColor.PrimaryBorder] = "Info", + [BitColor.SecondaryBorder] = "Info", + [BitColor.TertiaryBorder] = "Info" }; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.scss b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.scss index d94ab78dc8..66cc53f798 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.scss @@ -6,33 +6,41 @@ font-weight: 400; flex-flow: column; box-sizing: border-box; - font-size: spacing(1.5); justify-content: center; font-family: $tg-font-family; border-width: $shp-border-width; border-style: $shp-border-style; border-radius: $shp-border-radius; + font-size: var(--bit-msg-fontsize); + box-shadow: var(--bit-msg-boxshadow); +} + +.bit-msg-rct { + display: flex; + align-items: start; } .bit-msg-con { width: 100%; display: flex; + overflow: hidden; align-items: start; line-height: normal; padding: 0 spacing(0.5); + justify-content: var(--bit-msg-justifycontent); } .bit-msg-ict { display: flex; - margin: spacing(1); + margin: var(--bit-msg-ico-margin); + margin-inline-end: 0; } .bit-msg-ico { - font-size: spacing(2); + font-size: var(--bit-msg-ico-fontsize); } .bit-msg-cnc { - flex: 1; display: flex; overflow: hidden; align-items: center; @@ -48,8 +56,8 @@ flex-grow: 1; min-width: 0; display: flex; - margin: spacing(1); flex-direction: column; + margin: var(--bit-msg-margin); } .bit-msg-cnt { @@ -103,41 +111,108 @@ } .bit-msg-pri { - --bit-msg-clr: #{$clr-pri-text}; --bit-msg-clr-bg: #{$clr-pri}; + --bit-msg-clr: #{$clr-pri-text}; } .bit-msg-sec { - --bit-msg-clr: #{$clr-sec-text}; --bit-msg-clr-bg: #{$clr-sec}; + --bit-msg-clr: #{$clr-sec-text}; } .bit-msg-ter { - --bit-msg-clr: #{$clr-ter-text}; --bit-msg-clr-bg: #{$clr-ter}; + --bit-msg-clr: #{$clr-ter-text}; } .bit-msg-inf { - --bit-msg-clr: #{$clr-inf-text}; --bit-msg-clr-bg: #{$clr-inf}; + --bit-msg-clr: #{$clr-inf-text}; } .bit-msg-suc { - --bit-msg-clr: #{$clr-suc-text}; --bit-msg-clr-bg: #{$clr-suc}; + --bit-msg-clr: #{$clr-suc-text}; } .bit-msg-wrn { - --bit-msg-clr: #{$clr-wrn-text}; --bit-msg-clr-bg: #{$clr-wrn}; + --bit-msg-clr: #{$clr-wrn-text}; } .bit-msg-swr { - --bit-msg-clr: #{$clr-swr-text}; --bit-msg-clr-bg: #{$clr-swr}; + --bit-msg-clr: #{$clr-swr-text}; } .bit-msg-err { - --bit-msg-clr: #{$clr-err-text}; --bit-msg-clr-bg: #{$clr-err}; + --bit-msg-clr: #{$clr-err-text}; } + + +.bit-msg-pbg { + --bit-msg-clr: #{$clr-fg-pri}; + --bit-msg-clr-bg: #{$clr-bg-pri}; +} + +.bit-msg-sbg { + --bit-msg-clr: #{$clr-fg-pri}; + --bit-msg-clr-bg: #{$clr-bg-sec}; +} + +.bit-msg-tbg { + --bit-msg-clr: #{$clr-fg-pri}; + --bit-msg-clr-bg: #{$clr-bg-ter}; +} + +.bit-msg-pfg { + --bit-msg-clr: #{$clr-bg-pri}; + --bit-msg-clr-bg: #{$clr-fg-pri}; +} + +.bit-msg-sfg { + --bit-msg-clr: #{$clr-bg-pri}; + --bit-msg-clr-bg: #{$clr-fg-sec}; +} + +.bit-msg-tfg { + --bit-msg-clr: #{$clr-bg-pri}; + --bit-msg-clr-bg: #{$clr-fg-ter}; +} + +.bit-msg-pbr { + --bit-msg-clr: #{$clr-fg-pri}; + --bit-msg-clr-bg: #{$clr-brd-pri}; +} + +.bit-msg-sbr { + --bit-msg-clr: #{$clr-bg-pri}; + --bit-msg-clr-bg: #{$clr-brd-sec}; +} + +.bit-msg-tbr { + --bit-msg-clr: #{$clr-bg-pri}; + --bit-msg-clr-bg: #{$clr-brd-ter}; +} + +.bit-msg-sm { + --bit-msg-margin: #{spacing(0.5)}; + --bit-msg-fontsize: #{spacing(1.5)}; + --bit-msg-ico-margin: #{spacing(1)}; + --bit-msg-ico-fontsize: #{spacing(1.5)}; +} + +.bit-msg-md { + --bit-msg-margin: #{spacing(1)}; + --bit-msg-fontsize: #{spacing(1.75)}; + --bit-msg-ico-margin: #{spacing(1.2)}; + --bit-msg-ico-fontsize: #{spacing(2)}; +} + +.bit-msg-lg { + --bit-msg-margin: #{spacing(2)}; + --bit-msg-fontsize: #{spacing(2.0)}; + --bit-msg-ico-margin: #{spacing(2)}; + --bit-msg-ico-fontsize: #{spacing(2.5)}; +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessageClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessageClassStyles.cs index 4ea0ffcdcb..3063ff98e4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessageClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessageClassStyles.cs @@ -8,7 +8,12 @@ public class BitMessageClassStyles public string? Root { get; set; } /// <summary> - /// Custom CSS classes/styles for the main container of the BitMessage. + /// Custom CSS classes/styles for the root container of the BitMessage. + /// </summary> + public string? RootContainer { get; set; } + + /// <summary> + /// Custom CSS classes/styles for the icon and content container of the BitMessage. /// </summary> public string? Container { get; set; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor index a216a05a93..d5b4cad271 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor @@ -1,6 +1,10 @@ @namespace Bit.BlazorUI @inherits BitComponentBase +@{ + var presentationIcon = GetPresentationIcon(); +} + <div @ref="RootElement" @attributes="HtmlAttributes" id="@_Id" style="@StyleBuilder.Value" @@ -8,10 +12,6 @@ dir="@Dir?.ToString().ToLower()"> <div style="@Styles?.CoinContainer" class="bit-prs-cin @Classes?.CoinContainer" role="presentation"> - @{ - var presentationIcon = GetPresentationIcon(); - } - @if (Size is BitPersonaSize.Size8) { if (Presence is BitPersonaPresence.None) @@ -35,7 +35,7 @@ { if (OnImageClick.HasDelegate) { - <div style="@GetImageOverlayStyle()" class="bit-prs-imo @Classes?.ImageOverlay"> + <div style="@Styles?.ImageOverlay" class="bit-prs-imo @GetCoinClass() @Classes?.ImageOverlay"> @if (ImageOverlayTemplate is not null) { @ImageOverlayTemplate @@ -49,7 +49,7 @@ if ((ShowInitialsUntilImageLoads && _isLoaded is false) || _hasError) { - <span style="@Styles?.Initials" class="@Classes?.Initials"> + <span style="@Styles?.Initials" class="bit-prs-ini @Classes?.Initials"> @GetInitials() </span> } @@ -62,7 +62,7 @@ { if (_hasError is false) { - string dimension = GetPersonaImageDimension(); + var dimension = GetPersonaImageDimension(); <img src="@ImageUrl" style="display:@(_isLoaded ? "unset": "none");@Styles?.Image" @@ -77,7 +77,9 @@ } else { - @GetInitials() + <span style="@Styles?.Initials" class="bit-prs-ini @Classes?.Initials"> + @GetInitials() + </span> } </div> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor.cs index 09a65f7e42..a9690a3143 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.razor.cs @@ -27,6 +27,18 @@ public partial class BitPersona : BitComponentBase /// </summary> [Parameter] public BitPersonaClassStyles? Classes { get; set; } + /// <summary> + /// The background color when the user's initials are displayed. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColor? CoinColor { get; set; } + + /// <summary> + /// The shape of the coin. + /// </summary> + [Parameter, ResetClassBuilder] + public BitPersonaCoinShape? CoinShape { get; set; } + /// <summary> /// Optional custom persona coin size in pixel. /// </summary> @@ -38,9 +50,15 @@ public partial class BitPersona : BitComponentBase [Parameter] public RenderFragment? CoinTemplate { get; set; } /// <summary> - /// The background color when the user's initials are displayed. + /// The variant of the coin. + /// </summary> + [Parameter] public BitVariant? CoinVariant { get; set; } + + /// <summary> + /// Renders the persona in full width of its container element. /// </summary> - [Parameter] public string? Color { get; set; } + [Parameter, ResetClassBuilder] + public bool FullWidth { get; set; } /// <summary> /// Whether to not render persona details, and just render the persona image/initials. @@ -70,7 +88,7 @@ public partial class BitPersona : BitComponentBase /// <summary> /// Url to the image to use, should be a square aspect ratio and big enough to fit in the image area. /// </summary> - [Parameter] + [Parameter, ResetClassBuilder] [CallOnSet(nameof(OnSetImageUrl))] public string? ImageUrl { get; set; } @@ -172,6 +190,8 @@ protected override void RegisterCssClasses() { ClassBuilder.Register(() => Classes?.Root); + ClassBuilder.Register(() => FullWidth ? "bit-prs-fwi" : string.Empty); + ClassBuilder.Register(() => Size switch { BitPersonaSize.Size8 => "bit-prs-s8", @@ -186,9 +206,38 @@ protected override void RegisterCssClasses() _ => string.Empty }); - ClassBuilder.Register(() => Unknown && Size is not BitPersonaSize.Size8 ? "bit-prs-unk" : string.Empty); - ClassBuilder.Register(() => OnImageClick.HasDelegate ? "bit-prs-iac" : string.Empty); + + ClassBuilder.Register(() => ImageUrl.HasValue() ? "bit-prs-him" : string.Empty); + + ClassBuilder.Register(() => Size is BitPersonaSize.Size8 ? string.Empty : CoinColor switch + { + BitColor.Primary => "bit-prs-pri", + BitColor.Secondary => "bit-prs-sec", + BitColor.Tertiary => "bit-prs-ter", + BitColor.Info => "bit-prs-inf", + BitColor.Success => "bit-prs-suc", + BitColor.Warning => "bit-prs-wrn", + BitColor.SevereWarning => "bit-prs-swr", + BitColor.Error => "bit-prs-err", + BitColor.PrimaryBackground => "bit-prs-pbg", + BitColor.SecondaryBackground => "bit-prs-sbg", + BitColor.TertiaryBackground => "bit-prs-tbg", + BitColor.PrimaryForeground => "bit-prs-pfg", + BitColor.SecondaryForeground => "bit-prs-sfg", + BitColor.TertiaryForeground => "bit-prs-tfg", + BitColor.PrimaryBorder => "bit-prs-pbr", + BitColor.SecondaryBorder => "bit-prs-sbr", + BitColor.TertiaryBorder => "bit-prs-tbr", + _ => "bit-prs-inf" + }); + + ClassBuilder.Register(() => CoinShape switch + { + BitPersonaCoinShape.Circular => "bit-prs-crl", + BitPersonaCoinShape.Square => "bit-prs-sqr", + _ => "bit-prs-crl" + }); } protected override void RegisterCssStyles() @@ -216,8 +265,14 @@ protected override void RegisterCssStyles() { if (CoinSize is null) return null; + string? position = null; var presentationSize = CoinSize.Value / 3D; - return $"width:{presentationSize}px;height:{presentationSize}px;{Styles?.Presence?.Trim(';')}"; + if (CoinShape == BitPersonaCoinShape.Square) + { + var presentationPosition = presentationSize / 3D; + position = $"right:-{presentationPosition}px;bottom:-{presentationPosition}px;"; + } + return $"width:{presentationSize}px;height:{presentationSize}px;{position}{Styles?.Presence?.Trim(';')}"; } private string? GetPresentationIcon() @@ -232,12 +287,15 @@ protected override void RegisterCssStyles() return null; } - private string? GetCoinBgColorStyle() + private string? GetCoinClass() { - if (Size is BitPersonaSize.Size8) return null; - if (Color.HasNoValue()) return null; - - return $"background-color:{Color};"; + return CoinVariant switch + { + BitVariant.Fill => "bit-prs-fil", + BitVariant.Outline => "bit-prs-otl", + BitVariant.Text => "bit-prs-txt", + _ => "bit-prs-fil" + }; } private string? GetCoinWidthStyle() @@ -303,22 +361,14 @@ private string GetPersonaImageDimension() private string? GetImageContainerClass() { - var klass = $"{(CoinTemplate is null ? "bit-prs-imc" : null)} {Classes?.ImageContainer}".Trim(); + var klass = $"{(CoinTemplate is null ? "bit-prs-imc" : null)} {GetCoinClass()} {Classes?.ImageContainer}".Trim(); return klass.HasValue() ? klass : null; } private string? GetImageContainerStyle() { var coinWidthStyle = GetCoinWidthStyle(); - var coinBgColorStyle = GetCoinBgColorStyle(); - var style = $"{coinWidthStyle}{coinBgColorStyle}{Styles?.ImageContainer?.Trim(';')}"; - return style.HasValue() ? style : null; - } - - private string? GetImageOverlayStyle() - { - var coinBgColorStyle = GetCoinBgColorStyle(); - var style = $"{coinBgColorStyle}{Styles?.ImageOverlay?.Trim(';')}"; + var style = $"{coinWidthStyle}{Styles?.ImageContainer?.Trim(';')}"; return style.HasValue() ? style : null; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.scss b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.scss index 525d7f9077..138ff4e59f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersona.scss @@ -1,25 +1,29 @@ @import "../../../Styles/functions.scss"; .bit-prs { - margin: 0; - padding: 0; display: flex; font-weight: 400; box-shadow: none; + color: $clr-fg-pri; + width: fit-content; position: relative; align-items: center; box-sizing: border-box; + justify-content: center; font-size: spacing(1.75); - color: $clr-fg-pri; font-family: $tg-font-family; &.bit-dis { - pointer-events: none; color: $clr-fg-dis; + pointer-events: none; background-color: $clr-bg-dis; } } +.bit-prs-fwi { + width: 100%; +} + .bit-prs-cin { font-weight: 400; position: relative; @@ -31,10 +35,29 @@ overflow: hidden; font-weight: 600; position: relative; - border-radius: 50%; align-items: center; justify-content: center; - background-color: $clr-pri; + border-width: $shp-border-width; + border-style: $shp-border-style; +} + +.bit-prs-him { + .bit-prs-imc { + border: none; + border-width: 0; + } +} + +.bit-prs-crl { + .bit-prs-imc { + border-radius: 50%; + } +} + +.bit-prs-sqr { + .bit-prs-imc { + border-radius: $shp-border-radius; + } } .bit-prs-imo { @@ -47,7 +70,12 @@ justify-content: center; font-size: spacing(1.75); transition: all 0.1s linear; - background-color: $clr-pri; + color: var(--bit-prs-coin-clr-txt); + background-color: var(--bit-prs-coin-clr-bg); +} + +.bit-prs-ini { + text-transform: uppercase; } .bit-prs-img { @@ -64,8 +92,8 @@ width: spacing(1); height: spacing(1); position: absolute; - align-items: center; border-radius: 50%; + align-items: center; right: spacing(-0.25); bottom: spacing(-0.25); font-size: spacing(0.5); @@ -76,14 +104,6 @@ border: spacing(0.125) $shp-border-style $clr-brd-sec; } -.bit-prs-unk { - color: $clr-err; - - .bit-prs-imc { - background-color: $clr-bg-sec; - } -} - .bit-prs-det { display: flex; overflow: hidden; @@ -97,9 +117,9 @@ .bit-prs-ttx, .bit-prs-otx { overflow: hidden; + color: $clr-fg-pri; white-space: nowrap; text-overflow: ellipsis; - color: $clr-fg-pri; } .bit-prs-abt { @@ -122,8 +142,8 @@ justify-content: center; font-size: spacing(1.75); transform: translateX(-50%); - background-color: transparent; font-family: $tg-font-family; + background-color: transparent; @media (hover: hover) { &:hover { @@ -201,9 +221,6 @@ } .bit-prs-s24 { - min-height: spacing(3); - min-width: spacing(3); - .bit-prs-ima { flex: 0 0 auto; width: spacing(3); @@ -230,9 +247,6 @@ } .bit-prs-s32 { - min-height: spacing(4); - min-width: spacing(4); - .bit-prs-ima { flex: 0 0 auto; width: spacing(4); @@ -259,9 +273,6 @@ } .bit-prs-s40 { - min-height: spacing(5); - min-width: spacing(5); - .bit-prs-ima { flex: 0 0 auto; width: spacing(5); @@ -303,12 +314,16 @@ .bit-prs-otx { display: none; } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-0.4); + bottom: spacing(-0.4); + } + } } .bit-prs-s48 { - min-height: spacing(6); - min-width: spacing(6); - .bit-prs-ima { flex: 0 0 auto; width: spacing(6); @@ -350,12 +365,16 @@ .bit-prs-otx { display: none; } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-0.4); + bottom: spacing(-0.4); + } + } } .bit-prs-s56 { - min-height: spacing(7); - min-width: spacing(7); - .bit-prs-ima { flex: 0 0 auto; width: spacing(7); @@ -397,12 +416,16 @@ .bit-prs-otx { display: none; } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-0.5); + bottom: spacing(-0.5); + } + } } .bit-prs-s72 { - min-height: spacing(9); - min-width: spacing(9); - .bit-prs-ima { flex: 0 0 auto; width: spacing(9); @@ -446,12 +469,16 @@ .bit-prs-otx { display: none; } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-1); + bottom: spacing(-1); + } + } } .bit-prs-s100 { - min-height: spacing(12.5); - min-width: spacing(12.5); - .bit-prs-ima { flex: 0 0 auto; position: relative; @@ -497,12 +524,16 @@ font-weight: 400; font-size: spacing(1.75); } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-1); + bottom: spacing(-1); + } + } } .bit-prs-s120 { - min-height: spacing(15); - min-width: spacing(15); - .bit-prs-ima { flex: 0 0 auto; width: spacing(15); @@ -548,4 +579,115 @@ font-weight: 400; font-size: spacing(1.75); } + + &.bit-prs-sqr { + .bit-prs-pre { + right: spacing(-1.5); + bottom: spacing(-1.5); + } + } +} + +.bit-prs-fil { + color: var(--bit-prs-coin-clr-txt); + border-color: var(--bit-prs-coin-clr-bg); + background-color: var(--bit-prs-coin-clr-bg); +} + +.bit-prs-otl { + background-color: transparent; + color: var(--bit-prs-coin-clr-bg); + border-color: var(--bit-prs-coin-clr-bg); +} + +.bit-prs-txt { + border-color: transparent; + background-color: transparent; + color: var(--bit-prs-coin-clr-bg); +} + +.bit-prs-pri { + --bit-prs-coin-clr-bg: #{$clr-pri}; + --bit-prs-coin-clr-txt: #{$clr-pri-text}; +} + +.bit-prs-sec { + --bit-prs-coin-clr-bg: #{$clr-sec}; + --bit-prs-coin-clr-txt: #{$clr-sec-text}; +} + +.bit-prs-ter { + --bit-prs-coin-clr-bg: #{$clr-ter}; + --bit-prs-coin-clr-txt: #{$clr-ter-text}; +} + +.bit-prs-inf { + --bit-prs-coin-clr-bg: #{$clr-inf}; + --bit-prs-coin-clr-txt: #{$clr-inf-text}; +} + +.bit-prs-suc { + --bit-prs-coin-clr-bg: #{$clr-suc}; + --bit-prs-coin-clr-txt: #{$clr-suc-text}; +} + +.bit-prs-wrn { + --bit-prs-coin-clr-bg: #{$clr-wrn}; + --bit-prs-coin-clr-txt: #{$clr-wrn-text}; +} + +.bit-prs-swr { + --bit-prs-coin-clr-bg: #{$clr-swr}; + --bit-prs-coin-clr-txt: #{$clr-swr-text}; +} + +.bit-prs-err { + --bit-prs-coin-clr-bg: #{$clr-err}; + --bit-prs-coin-clr-txt: #{$clr-err-text}; +} + + +.bit-prs-pbg { + --bit-prs-coin-clr-bg: #{$clr-bg-pri}; + --bit-prs-coin-clr-txt: #{$clr-fg-pri}; +} + +.bit-prs-sbg { + --bit-prs-coin-clr-bg: #{$clr-bg-sec}; + --bit-prs-coin-clr-txt: #{$clr-fg-pri}; +} + +.bit-prs-tbg { + --bit-prs-coin-clr-bg: #{$clr-bg-ter}; + --bit-prs-coin-clr-txt: #{$clr-fg-pri}; +} + +.bit-prs-pfg { + --bit-prs-coin-clr-bg: #{$clr-fg-pri}; + --bit-prs-coin-clr-txt: #{$clr-bg-pri}; +} + +.bit-prs-sfg { + --bit-prs-coin-clr-bg: #{$clr-fg-sec}; + --bit-prs-coin-clr-txt: #{$clr-bg-pri}; +} + +.bit-prs-tfg { + --bit-prs-coin-clr-bg: #{$clr-fg-ter}; + --bit-prs-coin-clr-txt: #{$clr-bg-pri}; +} + +.bit-prs-pbr { + --bit-prs-coin-clr-bg: #{$clr-brd-pri}; + --bit-prs-coin-clr-txt: #{$clr-fg-pri}; +} + +.bit-prs-sbr { + --bit-prs-coin-clr-bg: #{$clr-brd-sec}; + --bit-prs-coin-clr-txt: #{$clr-bg-pri}; +} + +.bit-prs-tbr { + --bit-prs-coin-clr-bg: #{$clr-brd-ter}; + --bit-prs-coin-clr-txt: #{$clr-bg-pri}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersonaCoinShape.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersonaCoinShape.cs new file mode 100644 index 0000000000..7dc54ab1c1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Persona/BitPersonaCoinShape.cs @@ -0,0 +1,14 @@ +namespace Bit.BlazorUI; + +public enum BitPersonaCoinShape +{ + /// <summary> + /// Represents the traditional round shape of a coin. + /// </summary> + Circular, + + /// <summary> + /// Represents a square-shaped coin. + /// </summary> + Square +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor index 16ae24d1a6..9b67ef593a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor @@ -9,43 +9,49 @@ @foreach (var item in _items) { - <div style="@Styles?.Container @item.CssStyle" class="bit-snb-itm @Classes?.Container @GetItemClasses(item)"> + <div @key="item.Id" + style="@Styles?.Container @item.CssStyle" + class="bit-snb-itm @GetItemClasses(item) @Classes?.Container @item.CssClass"> <div style="@Styles?.Header" class="bit-snb-hdr @Classes?.Header"> <button @onclick="() => Dismiss(item)" type="button" style="@Styles?.DismissButton" class="bit-snb-cbt @Classes?.DismissButton"> - <i style="@Styles?.DismissIcon" class="bit-icon bit-icon--@DismissIconName @Classes?.DismissIcon" /> + <i style="@Styles?.DismissIcon" class="bit-icon bit-icon--@(DismissIconName ?? "Cancel") @Classes?.DismissIcon" /> </button> - @if (TitleTemplate is not null) - { - @TitleTemplate(item.Title) - } - else if (item.Title.HasValue()) - { - <label style="@Styles?.Title" class="bit-snb-ttl @Classes?.Title" title="@item.Title"> - @item.Title - </label> - } - </div> - - @if (BodyTemplate is not null) + @if (TitleTemplate is not null) { - @BodyTemplate(item.Body ?? string.Empty) + @TitleTemplate(item.Title) } - else if (item.Body.HasValue()) + else if (item.Title.HasValue()) { - <span style="@Styles?.Body" class="bit-snb-bdy @Classes?.Body"> - @item.Body - </span> + <label title="@item.Title" + style="@Styles?.Title" + class="bit-snb-ttl @(Multiline ? "" : "bit-snb-elp") @Classes?.Title"> + @item.Title + </label> } + </div> - @if (AutoDismiss) - { - <span style="animation-duration: @(FormattableString.Invariant($"{AutoDismissTime.TotalSeconds}s")); @Styles?.ProgressBar" - class="bit-snb-pbr @Classes?.ProgressBar"></span> - } + @if (BodyTemplate is not null) + { + @BodyTemplate(item.Body ?? string.Empty) + } + else if (item.Body.HasValue()) + { + <div style="@Styles?.Body" + class="bit-snb-bdy @(Multiline ? "" : "bit-snb-elp") @Classes?.Body"> + @item.Body + </div> + } + + @if (AutoDismiss) + { + <span style="animation-duration:@GetDuration(); @Styles?.ProgressBar" + class="bit-snb-prb @Classes?.ProgressBar"> + </span> + } </div> } </div> \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor.cs index cfe6b13301..fbfbfb5a90 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.razor.cs @@ -9,48 +9,53 @@ public partial class BitSnackBar : BitComponentBase /// <summary> - /// Whether or not to dismiss itself automatically. + /// Whether or not automatically dismiss the snack bar. /// </summary> - [Parameter] public bool AutoDismiss { get; set; } = true; + [Parameter] public bool AutoDismiss { get; set; } /// <summary> - /// How long the SnackBar will automatically dismiss (default is 3 seconds). + /// How long does it take to automatically dismiss the snack bar (default is 3 seconds). /// </summary> - [Parameter] public TimeSpan AutoDismissTime { get; set; } = TimeSpan.FromSeconds(3); + [Parameter] public TimeSpan? AutoDismissTime { get; set; } /// <summary> - /// Used to customize how content inside the Body is rendered. + /// Used to customize how the content inside the body is rendered. /// </summary> [Parameter] public RenderFragment<string>? BodyTemplate { get; set; } /// <summary> - /// Custom CSS classes for different parts of the BitSnackBar. + /// Custom CSS classes for different parts of the snack bar. /// </summary> [Parameter] public BitSnackBarClassStyles? Classes { get; set; } /// <summary> - /// Dismiss Icon in SnackBar. + /// The icon name of the dismiss button /// </summary> - [Parameter] public string DismissIconName { get; set; } = "Cancel"; + [Parameter] public string? DismissIconName { get; set; } /// <summary> - /// Callback for when the Dismissed. + /// Enables the multiline mode of both title and body. + /// </summary> + [Parameter] public bool Multiline { get; set; } + + /// <summary> + /// Callback for when any snack bar is dismissed. /// </summary> [Parameter] public EventCallback OnDismiss { get; set; } /// <summary> - /// The position of SnackBar to show. + /// The position of the snack bars to show (default is bottom right). /// </summary> [Parameter, ResetClassBuilder] - public BitSnackBarPosition Position { get; set; } = BitSnackBarPosition.BottomRight; + public BitSnackBarPosition? Position { get; set; } /// <summary> - /// Custom CSS styles for different parts of the BitSnackBar. + /// Custom CSS styles for different parts of the snack bar. /// </summary> [Parameter] public BitSnackBarClassStyles? Styles { get; set; } /// <summary> - /// Used to customize how content inside the Title is rendered. + /// Used to customize how content inside the title is rendered. /// </summary> [Parameter] public RenderFragment<string>? TitleTemplate { get; set; } @@ -59,20 +64,20 @@ public partial class BitSnackBar : BitComponentBase /// <summary> /// Shows the snackbar. /// </summary> - public async Task Show(string title, string? body = "", BitSnackBarType type = BitSnackBarType.None, string? cssClass = null, string? cssStyle = null) + public async Task Show(string title, string? body = "", BitColor color = BitColor.Info, string? cssClass = null, string? cssStyle = null) { var item = new BitSnackBarItem { Title = title, Body = body, - Type = type, + Color = color, CssClass = cssClass, CssStyle = cssStyle }; if (AutoDismiss) { - var timer = new System.Timers.Timer(AutoDismissTime.TotalMilliseconds); + var timer = new System.Timers.Timer((AutoDismissTime ?? TimeSpan.FromSeconds(3)).TotalMilliseconds); timer.Elapsed += (_, _) => { timer.Close(); @@ -87,24 +92,29 @@ public async Task Show(string title, string? body = "", BitSnackBarType type = B } /// <summary> - /// Shows the snackbar with Info type. + /// Shows the snackbar with Info color. /// </summary> - public Task Info(string title, string? body = "") => Show(title, body, BitSnackBarType.Info); + public Task Info(string title, string? body = "") => Show(title, body, BitColor.Info); /// <summary> - /// Shows the snackbar with Success type. + /// Shows the snackbar with Success color. /// </summary> - public Task Success(string title, string? body = "") => Show(title, body, BitSnackBarType.Success); + public Task Success(string title, string? body = "") => Show(title, body, BitColor.Success); /// <summary> - /// Shows the snackbar with Warning type. + /// Shows the snackbar with Warning color. /// </summary> - public Task Warning(string title, string? body = "") => Show(title, body, BitSnackBarType.Warning); + public Task Warning(string title, string? body = "") => Show(title, body, BitColor.Warning); /// <summary> - /// Shows the snackbar with Error type. + /// Shows the snackbar with SevereWarning color. /// </summary> - public Task Error(string title, string? body = "") => Show(title, body, BitSnackBarType.Error); + public Task SevereWarning(string title, string? body = "") => Show(title, body, BitColor.SevereWarning); + + /// <summary> + /// Shows the snackbar with Error color. + /// </summary> + public Task Error(string title, string? body = "") => Show(title, body, BitColor.Error); @@ -116,13 +126,13 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => Position switch { - BitSnackBarPosition.TopLeft => $"{RootElementClass}-tlf", - BitSnackBarPosition.TopCenter => $"{RootElementClass}-tcn", - BitSnackBarPosition.TopRight => $"{RootElementClass}-trt", - BitSnackBarPosition.BottomLeft => $"{RootElementClass}-blf", - BitSnackBarPosition.BottomCenter => $"{RootElementClass}-bcn", - BitSnackBarPosition.BottomRight => $"{RootElementClass}-brt", - _ => string.Empty + BitSnackBarPosition.TopStart => "bit-snb-tst", + BitSnackBarPosition.TopCenter => "bit-snb-tcn", + BitSnackBarPosition.TopEnd => "bit-snb-ten", + BitSnackBarPosition.BottomStart => "bit-snb-bst", + BitSnackBarPosition.BottomCenter => "bit-snb-bcn", + BitSnackBarPosition.BottomEnd => "bit-snb-ben", + _ => "bit-snb-ben" }); } @@ -142,25 +152,33 @@ private void Dismiss(BitSnackBarItem item) InvokeAsync(StateHasChanged); } - private string GetItemClasses(BitSnackBarItem item) + private string GetDuration() { - StringBuilder className = new StringBuilder(); - - className.Append(' ').Append(item.Type switch - { - BitSnackBarType.Info => $"{RootElementClass}-info", - BitSnackBarType.Warning => $"{RootElementClass}-warning", - BitSnackBarType.Success => $"{RootElementClass}-success", - BitSnackBarType.Error => $"{RootElementClass}-error", - BitSnackBarType.SevereWarning => $"{RootElementClass}-severe-warning", - _ => string.Empty - }); + return FormattableString.Invariant($"{(AutoDismissTime ?? TimeSpan.FromSeconds(3)).TotalSeconds}s"); + } - if (item.CssClass?.HasValue() ?? false) + private static string GetItemClasses(BitSnackBarItem item) + { + return item.Color switch { - className.Append(' ').Append(item.CssClass); - } - - return className.ToString(); + BitColor.Primary => "bit-snb-pri", + BitColor.Secondary => "bit-snb-sec", + BitColor.Tertiary => "bit-snb-ter", + BitColor.Info => "bit-snb-inf", + BitColor.Success => "bit-snb-suc", + BitColor.Warning => "bit-snb-wrn", + BitColor.SevereWarning => "bit-snb-swr", + BitColor.Error => "bit-snb-err", + BitColor.PrimaryBackground => "bit-snb-pbg", + BitColor.SecondaryBackground => "bit-snb-sbg", + BitColor.TertiaryBackground => "bit-snb-tbg", + BitColor.PrimaryForeground => "bit-snb-pfg", + BitColor.SecondaryForeground => "bit-snb-sfg", + BitColor.TertiaryForeground => "bit-snb-tfg", + BitColor.PrimaryBorder => "bit-snb-pbr", + BitColor.SecondaryBorder => "bit-snb-sbr", + BitColor.TertiaryBorder => "bit-snb-tbr", + _ => "bit-snb-inf" + }; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.scss b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.scss index 465cf6cea2..1c795a4695 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBar.scss @@ -3,10 +3,10 @@ .bit-snb { display: flex; position: fixed; - max-width: 100%; gap: spacing(1.25); flex-flow: column nowrap; z-index: $zindex-snackbar; + max-width: calc(100% - spacing(1)); @keyframes bit-snackbar-progress-animation { from { @@ -22,32 +22,27 @@ .bit-snb-itm { display: flex; gap: spacing(1); + width: fit-content; position: relative; padding: spacing(1.25); - min-width: spacing(27.5); + color: var(--bit-snb-fg); flex-flow: column nowrap; - min-height: spacing(8.75); border-radius: spacing(0.375); justify-content: space-between; - color: $clr-fg-pri; - background-color: $clr-bg-sec; + background-color: var(--bit-snb-bg); } -.bit-snb-pbr { - left: 0; +.bit-snb-prb { bottom: 0; width: 100%; position: absolute; - height: spacing(0.625); + height: spacing(0.5); + inset-inline-start: 0; + animation-timing-function: linear; + background-color: var(--bit-snb-fg); animation-name: bit-snackbar-progress-animation; } -.bit-snb-bdy { - overflow: auto; - white-space: pre; - font-size: spacing(1.5); -} - .bit-snb-hdr { display: flex; gap: spacing(1.25); @@ -56,62 +51,121 @@ } .bit-snb-ttl { - overflow: hidden; font-weight: 500; - white-space: nowrap; font-size: spacing(2); +} + +.bit-snb-bdy { + font-size: spacing(1.5); +} + +.bit-snb-elp { + overflow: hidden; + white-space: nowrap; text-overflow: ellipsis; } .bit-snb-cbt { cursor: pointer; + color: var(--bit-snb-fg); background-color: inherit; border-radius: spacing(0.375); } -.bit-snb-info { - background-color: $clr-inf; +.bit-snb-pri { + --bit-snb-bg: #{$clr-pri}; + --bit-snb-fg: #{$clr-pri-text}; +} - .bit-snb-pbr { - background-color: $clr-inf; - } +.bit-snb-sec { + --bit-snb-bg: #{$clr-sec}; + --bit-snb-fg: #{$clr-sec-text}; } -.bit-snb-success { - background-color: $clr-suc; +.bit-snb-ter { + --bit-snb-bg: #{$clr-ter}; + --bit-snb-fg: #{$clr-ter-text}; +} - .bit-snb-pbr { - background-color: $clr-suc; - } +.bit-snb-inf { + --bit-snb-bg: #{$clr-inf}; + --bit-snb-fg: #{$clr-inf-text}; } -.bit-snb-warning { - background-color: $clr-wrn; +.bit-snb-suc { + --bit-snb-bg: #{$clr-suc}; + --bit-snb-fg: #{$clr-suc-text}; +} - .bit-snb-pbr { - background-color: $clr-wrn; - } +.bit-snb-wrn { + --bit-snb-bg: #{$clr-wrn}; + --bit-snb-fg: #{$clr-wrn-text}; } -.bit-snb-severe-warning { - background-color: $clr-swr; +.bit-snb-swr { + --bit-snb-bg: #{$clr-swr}; + --bit-snb-fg: #{$clr-swr-text}; +} - .bit-snb-pbr { - background-color: $clr-swr; - } +.bit-snb-err { + --bit-snb-bg: #{$clr-err}; + --bit-snb-fg: #{$clr-err-text}; } -.bit-snb-error { - background-color: $clr-err; +.bit-snb-pbg { + --bit-snb-bg: #{$clr-bg-pri}; + --bit-snb-fg: #{$clr-fg-pri}; +} - .bit-snb-pbr { - background-color: $clr-err; - } +.bit-snb-pbg { + --bit-snb-bg: #{$clr-bg-pri}; + --bit-snb-fg: #{$clr-fg-pri}; +} + +.bit-snb-sbg { + --bit-snb-bg: #{$clr-bg-sec}; + --bit-snb-fg: #{$clr-fg-pri}; +} + +.bit-snb-tbg { + --bit-snb-bg: #{$clr-bg-ter}; + --bit-snb-fg: #{$clr-fg-pri}; +} + +.bit-snb-pfg { + --bit-snb-bg: #{$clr-fg-pri}; + --bit-snb-fg: #{$clr-bg-pri}; +} + +.bit-snb-sfg { + --bit-snb-bg: #{$clr-fg-sec}; + --bit-snb-fg: #{$clr-bg-pri}; } -.bit-snb-tlf { +.bit-snb-tfg { + --bit-snb-bg: #{$clr-fg-ter}; + --bit-snb-fg: #{$clr-bg-pri}; +} + +.bit-snb-pbr { + --bit-snb-bg: #{$clr-brd-pri}; + --bit-snb-fg: #{$clr-fg-pri}; +} + +.bit-snb-sbr { + --bit-snb-bg: #{$clr-brd-sec}; + --bit-snb-fg: #{$clr-fg-pri}; +} + +.bit-snb-tbr { + --bit-snb-bg: #{$clr-brd-ter}; + --bit-snb-fg: #{$clr-fg-pri}; +} + + +.bit-snb-tst { top: spacing(1); - left: spacing(1); + inset-inline: spacing(1); } .bit-snb-tcn { @@ -120,14 +174,15 @@ transform: translateX(-50%); } -.bit-snb-trt { +.bit-snb-ten { top: spacing(1); - right: spacing(1); + align-items: flex-end; + inset-inline: spacing(1); } -.bit-snb-blf { - left: spacing(1); +.bit-snb-bst { bottom: spacing(1); + inset-inline: spacing(1); } .bit-snb-bcn { @@ -136,7 +191,8 @@ transform: translateX(-50%); } -.bit-snb-brt { - right: spacing(1); +.bit-snb-ben { bottom: spacing(1); + align-items: flex-end; + inset-inline: spacing(1); } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarItem.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarItem.cs index f6a6e23b08..a26b5055bf 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarItem.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarItem.cs @@ -2,13 +2,15 @@ internal class BitSnackBarItem { + public readonly Guid Id = Guid.NewGuid(); + public string Title { get; set; } = default!; - + public string? Body { get; set; } - - public BitSnackBarType? Type { get; set; } - + + public BitColor? Color { get; set; } + public string? CssClass { get; set; } - + public string? CssStyle { get; set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarPosition.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarPosition.cs index b2e03ca158..11af39b8b9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarPosition.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarPosition.cs @@ -3,10 +3,10 @@ namespace Bit.BlazorUI; public enum BitSnackBarPosition { - TopLeft, + TopStart, TopCenter, - TopRight, - BottomLeft, + TopEnd, + BottomStart, BottomCenter, - BottomRight, + BottomEnd, } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarType.cs b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarType.cs deleted file mode 100644 index 081afd9f91..0000000000 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/SnackBar/BitSnackBarType.cs +++ /dev/null @@ -1,35 +0,0 @@ - -namespace Bit.BlazorUI; - -public enum BitSnackBarType -{ - /// <summary> - /// None styled SnackBar - /// </summary> - None, - - /// <summary> - /// Info styled SnackBar - /// </summary> - Info, - - /// <summary> - /// Warning styled SnackBar - /// </summary> - Warning, - - /// <summary> - /// Success styled SnackBar - /// </summary> - Success, - - /// <summary> - /// Error styled SnackBar - /// </summary> - Error, - - /// <summary> - /// Severe Warning styled SnackBar - /// </summary> - SevereWarning, -} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor index 52dfae9641..a9e3e8891c 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor @@ -24,16 +24,19 @@ <div style="@Styles?.Title" class="bit-acd-ttl @Classes?.Title"> @Title </div> - <div style="@Styles?.Description" class="bit-acd-des @Classes?.Description"> - @Description - </div> + @if (Description.HasValue()) + { + <div style="@Styles?.Description" class="bit-acd-des @Classes?.Description"> + @Description + </div> + } </div> <i style="@Styles?.ChevronDownIcon" - class="bit-icon bit-icon--ChevronDown bit-acd-hic@(IsExpanded ? " bit-acd-hex" : "") @Classes?.ChevronDownIcon" /> + class="bit-icon bit-icon--ChevronRight bit-acd-hic@(IsExpanded ? " bit-acd-hex" : "") @Classes?.ChevronDownIcon" /> } </button> - <div> + <div style="@Styles?.ContentContainer" class="bit-acd-cnt @Classes?.ContentContainer"> <div role="region" style="@Styles?.Content" class="bit-acd-con @(IsExpanded ? "bit-acd-cex" : "bit-acd-cco") @Classes?.Content" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor.cs index febe2ce4bd..81015ac827 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.razor.cs @@ -3,36 +3,47 @@ public partial class BitAccordion : BitComponentBase { /// <summary> - /// Custom CSS classes for different parts of the BitAccordion. + /// Alias for the ChildContent parameter. + /// </summary> + [Parameter] public RenderFragment? Body { get; set; } + + /// <summary> + /// Custom CSS classes for different parts of the accordion. /// </summary> [Parameter] public BitAccordionClassStyles? Classes { get; set; } /// <summary> - /// The content of the Accordion. + /// The content of the accordion. /// </summary> [Parameter] public RenderFragment? ChildContent { get; set; } /// <summary> - /// Default value of the IsExpanded. + /// Default value for the IsExpanded parameter. /// </summary> [Parameter] public bool? DefaultIsExpanded { get; set; } /// <summary> - /// A short description in the header of Accordion. + /// A short description in the header of the accordion. /// </summary> [Parameter] public string? Description { get; set; } /// <summary> - /// Used to customize how the header inside the Accordion is rendered. + /// Used to customize the header of the accordion. /// </summary> [Parameter] public RenderFragment<bool>? HeaderTemplate { get; set; } /// <summary> - /// Determines whether the accordion is expanding or collapses. + /// Determines whether the accordion is expanded or collapsed. /// </summary> [Parameter, ResetClassBuilder, ResetStyleBuilder, TwoWayBound] public bool IsExpanded { get; set; } + /// <summary> + /// Removes the default border of the accordion and gives a background color to the body. + /// </summary> + [Parameter, ResetClassBuilder] + public bool NoBorder { get; set; } + /// <summary> /// Callback that is called when the header is clicked. /// </summary> @@ -44,16 +55,17 @@ public partial class BitAccordion : BitComponentBase [Parameter] public EventCallback<bool> OnChange { get; set; } /// <summary> - /// Custom CSS styles for different parts of the BitAccordion. + /// Custom CSS styles for different parts of the accordion. /// </summary> [Parameter] public BitAccordionClassStyles? Styles { get; set; } /// <summary> - /// Title in the header of Accordion. + /// Title in the header of accordion. /// </summary> [Parameter] public string? Title { get; set; } + protected override string RootElementClass => "bit-acd"; protected override void RegisterCssClasses() @@ -61,6 +73,8 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => Classes?.Root); ClassBuilder.Register(() => IsExpanded ? Classes?.Expanded : string.Empty); + + ClassBuilder.Register(() => NoBorder ? "bit-acd-nbd" : string.Empty); } protected override void RegisterCssStyles() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.scss b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.scss index 77a805f6ec..6fa72c4218 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordion.scss @@ -4,13 +4,13 @@ width: 100%; display: flex; font-weight: 400; + color: $clr-fg-pri; box-sizing: border-box; font-size: spacing(1.75); flex-flow: column nowrap; - color: $clr-fg-pri; - border-radius: $shp-border-radius; font-family: $tg-font-family; background-color: $clr-bg-pri; + border-radius: $shp-border-radius; border: $shp-border-width $shp-border-style $clr-brd-pri; &.bit-dis { @@ -24,20 +24,30 @@ } } +.bit-acd-nbd { + border: none; + background-color: $clr-bg-sec; + + .bit-acd-cnt { + background-color: $clr-bg-sec; + } +} + .bit-acd-hdr { display: flex; cursor: pointer; text-align: start; + color: $clr-fg-pri; align-items: center; padding: spacing(1.5); background-color: transparent; - color: $clr-fg-pri; } .bit-acd-hic { display: flex; align-items: center; margin: 0 spacing(0.625); + transform: rotate(90deg); transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; } @@ -51,17 +61,17 @@ } .bit-acd-ttl { - min-width: 30%; font-weight: 600; font-size: spacing(2); } .bit-acd-des { + flex-grow: 1; color: $clr-fg-sec; } .bit-acd-hex { - transform: rotate(-180deg); + transform: rotate(-90deg); } .bit-acd-con { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordionClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordionClassStyles.cs index e1f947a3ec..d232b7f68d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordionClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Accordion/BitAccordionClassStyles.cs @@ -37,6 +37,11 @@ public class BitAccordionClassStyles /// </summary> public string? ChevronDownIcon { get; set; } + /// <summary> + /// Custom CSS classes/styles for the content container of the BitAccordion. + /// </summary> + public string? ContentContainer { get; set; } + /// <summary> /// Custom CSS classes/styles for the content of the BitAccordion. /// </summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor index 0f43e0a8f0..ecd32ff0b8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor @@ -58,18 +58,25 @@ <div style="@Styles?.ButtonsContainer" class="bit-dlg-bct @Classes?.ButtonsContainer"> @if (ShowOkButton) { - <button style="@Styles?.OkButton" - class="bit-dlg-okb @Classes?.OkButton" - @onclick="HandleOnOkClick"> - @OkText + <button @onclick="HandleOnOkClick" + style="@Styles?.OkButton" + class="bit-dlg-okb @Classes?.OkButton"> + @if (_isLoading) + { + <div style="@Styles?.Spinner" class="bit-dlg-spn @Classes?.Spinner"></div> + } + else + { + @OkText + } </button> } @if (ShowCancelButton) { - <button style="@Styles?.CancelButton" - class="bit-dlg-cnb @Classes?.CancelButton" - @onclick="HandleOnCancelClick"> + <button @onclick="HandleOnCancelClick" + style="@Styles?.CancelButton" + class="bit-dlg-cnb @Classes?.CancelButton"> @CancelText </button> } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs index 362c536e43..5ae699aba7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs @@ -4,6 +4,7 @@ public partial class BitDialog : BitComponentBase, IAsyncDisposable { private int _offsetTop; private bool _disposed; + private bool _isLoading; private bool _internalIsOpen; private string _containerId = default!; private TaskCompletionSource<BitDialogResult?>? _tcs = new(); @@ -17,7 +18,7 @@ public partial class BitDialog : BitComponentBase, IAsyncDisposable /// <summary> /// Enables the auto scrollbar toggle behavior of the Dialog. /// </summary> - [Parameter] public bool AutoToggleScroll { get; set; } = true; + [Parameter] public bool AutoToggleScroll { get; set; } /// <summary> /// When true, the Dialog will be positioned absolute instead of fixed. @@ -25,7 +26,7 @@ public partial class BitDialog : BitComponentBase, IAsyncDisposable [Parameter] public bool AbsolutePosition { get; set; } /// <summary> - /// Alias for childcontent + /// Alias for child content. /// </summary> [Parameter] public RenderFragment? Body { get; set; } @@ -260,7 +261,7 @@ private async Task HandleOnOverlayClick(MouseEventArgs e) private async Task HandleOnCloseClick(MouseEventArgs e) { - _ = OnClose.InvokeAsync(e); + await OnClose.InvokeAsync(e); await DismissDialog(e); } @@ -272,7 +273,7 @@ private async Task HandleOnCancelClick(MouseEventArgs e) _tcs?.SetResult(Result); _tcs = null; - _ = OnCancel.InvokeAsync(e); + await OnCancel.InvokeAsync(e); await DismissDialog(e); } @@ -284,9 +285,18 @@ private async Task HandleOnOkClick(MouseEventArgs e) _tcs?.SetResult(Result); _tcs = null; - _ = OnOk.InvokeAsync(e); + _isLoading = true; - await DismissDialog(e); + try + { + await OnOk.InvokeAsync(e); + + await DismissDialog(e); + } + finally + { + _isLoading = false; + } } private string GetPositionClass() => Position switch diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.scss b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.scss index 931afd9e4e..a91c080711 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.scss @@ -9,6 +9,16 @@ z-index: $zindex-modal; font-size: spacing(1.75); font-family: $tg-font-family; + + @keyframes bit-dlg-spinner-animation { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } } .bit-dlg-nta { @@ -98,12 +108,14 @@ background-color: $clr-bg-pri; button { + display: flex; cursor: pointer; text-decoration: none; min-width: spacing(8); min-height: spacing(4); box-sizing: border-box; margin: 0 spacing(0.5); + justify-content: center; font-size: spacing(1.75); padding: spacing(0.5) spacing(2); border-width: $shp-border-width; @@ -132,6 +144,18 @@ } } +.bit-dlg-spn { + border-radius: 50%; + width: spacing(2.5); + height: spacing(2.5); + border-color: $clr-brd-sec; + border-width: spacing(0.2125); + border-style: $shp-border-style; + border-top-color: $clr-pri-dark; + animation: bit-dlg-spinner-animation 1.3s linear infinite; + animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67); +} + .bit-dlg-cnb { color: $clr-pri; border-color: $clr-pri; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialogClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialogClassStyles.cs index cd14c6eef0..67c6acd9a7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialogClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialogClassStyles.cs @@ -42,6 +42,11 @@ public class BitDialogClassStyles /// </summary> public string? ButtonsContainer { get; set; } + /// <summary> + /// Custom CSS classes/styles for the loading spinner of the ok button of the BitDialog. + /// </summary> + public string? Spinner { get; set; } + /// <summary> /// Custom CSS classes/styles for the ok button of the BitDialog. /// </summary> diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor index 8b282ce656..cda47ae2ef 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor @@ -8,20 +8,21 @@ style="@StyleBuilder.Value" class="@ClassBuilder.Value" dir="@Dir?.ToString().ToLower()" - role=@((IsAlert ?? (IsBlocking && IsModeless is false))? "alertdialog": "dialog") - aria-modal="@((IsModeless is false).ToString())" aria-labelledby="@TitleAriaId" - aria-describedby="@SubtitleAriaId"> - <div style="@Styles?.Container" class="bit-mdl-doc @GetPositionClass() @Classes?.Container" role="document"> - @if (IsModeless is false) - { - <div style="@Styles?.Overlay" class="bit-mdl-ovl @Classes?.Overlay" aria-hidden="true" @onclick="CloseModal" /> - } - <div id="@_containerId" style="@Styles?.Content" class="bit-mdl-ctn @Classes?.Content"> - <div style="@Styles?.ScrollContent" class="bit-mdl-scn @Classes?.ScrollContent"> - @ChildContent - </div> - </div> + aria-describedby="@SubtitleAriaId" + aria-modal="@((Modeless is false).ToString())" + role=@((IsAlert ?? (Blocking && Modeless is false))? "alertdialog": "dialog")> + @if (Modeless is false) + { + <div @onclick="CloseModal" + aria-hidden="true" + style="@Styles?.Overlay" + class="bit-mdl-ovl @Classes?.Overlay" /> + } + <div id="@_containerId" + style="@Styles?.Content" + class="bit-mdl-ctn @Classes?.Content"> + @ChildContent </div> </div> } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs index fdac7b2117..a63b8b8516 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs @@ -16,7 +16,7 @@ public partial class BitModal : BitComponentBase, IAsyncDisposable /// <summary> /// Enables the auto scrollbar toggle behavior of the Modal. /// </summary> - [Parameter] public bool AutoToggleScroll { get; set; } = true; + [Parameter] public bool AutoToggleScroll { get; set; } /// <summary> /// When true, the Modal will be positioned absolute instead of fixed. @@ -24,6 +24,11 @@ public partial class BitModal : BitComponentBase, IAsyncDisposable [Parameter, ResetClassBuilder] public bool AbsolutePosition { get; set; } + /// <summary> + /// Whether the Modal can be light dismissed by clicking outside the Modal (on the overlay). + /// </summary> + [Parameter] public bool Blocking { get; set; } + /// <summary> /// The content of the Modal, it can be any custom tag or text. /// </summary> @@ -40,24 +45,32 @@ public partial class BitModal : BitComponentBase, IAsyncDisposable [Parameter] public string? DragElementSelector { get; set; } /// <summary> - /// Determines the ARIA role of the Modal (alertdialog/dialog). If this is set, it will override the ARIA role determined by IsBlocking and IsModeless. + /// Whether the Modal can be dragged around. /// </summary> - [Parameter] public bool? IsAlert { get; set; } + [Parameter] public bool Draggable { get; set; } /// <summary> - /// Whether the modal can be light dismissed by clicking outside the Modal (on the overlay). + /// Makes the Modal height 100% of its parent container. /// </summary> - [Parameter] public bool IsBlocking { get; set; } + [Parameter, ResetClassBuilder] + public bool FullHeight { get; set; } /// <summary> - /// Whether the Modal can be dragged around. + /// Makes the Modal width and height 100% of its parent container. /// </summary> - [Parameter] public bool IsDraggable { get; set; } + [Parameter, ResetClassBuilder] + public bool FullSize { get; set; } /// <summary> - /// Whether the Modal should be modeless (e.g. not dismiss when focusing/clicking outside of the Modal). if true: IsBlocking is ignored, there will be no overlay. + /// Makes the Modal width 100% of its parent container. + /// </summary> + [Parameter, ResetClassBuilder] + public bool FullWidth { get; set; } + + /// <summary> + /// Determines the ARIA role of the Modal (alertdialog/dialog). If this is set, it will override the ARIA role determined by Blocking and Modeless. /// </summary> - [Parameter] public bool IsModeless { get; set; } + [Parameter] public bool? IsAlert { get; set; } /// <summary> /// Whether the Modal is displayed. @@ -65,15 +78,21 @@ public partial class BitModal : BitComponentBase, IAsyncDisposable [Parameter, TwoWayBound] public bool IsOpen { get; set; } + /// <summary> + /// Whether the Modal should be modeless (e.g. not dismiss when focusing/clicking outside of the Modal). if true: IsBlocking is ignored, there will be no overlay. + /// </summary> + [Parameter] public bool Modeless { get; set; } + /// <summary> /// A callback function for when the Modal is dismissed light dismiss, before the animation completes. /// </summary> [Parameter] public EventCallback<MouseEventArgs> OnDismiss { get; set; } /// <summary> - /// Position of the modal on the screen. + /// Position of the Modal on the screen. /// </summary> - [Parameter] public BitModalPosition Position { get; set; } = BitModalPosition.Center; + [Parameter, ResetClassBuilder] + public BitModalPosition? Position { get; set; } /// <summary> /// Set the element selector for which the Modal disables its scroll if applicable. @@ -104,6 +123,23 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => Classes?.Root); ClassBuilder.Register(() => AbsolutePosition ? "bit-mdl-abs" : string.Empty); + + ClassBuilder.Register(() => FullSize || FullHeight ? "bit-mdl-fhe" : string.Empty); + ClassBuilder.Register(() => FullSize || FullWidth ? "bit-mdl-fwi" : string.Empty); + + ClassBuilder.Register(() => Position switch + { + BitModalPosition.Center => "bit-mdl-ctr", + BitModalPosition.TopLeft => "bit-mdl-tl", + BitModalPosition.TopCenter => "bit-mdl-tc", + BitModalPosition.TopRight => "bit-mdl-tr", + BitModalPosition.CenterLeft => "bit-mdl-cl", + BitModalPosition.CenterRight => "bit-mdl-cr", + BitModalPosition.BottomLeft => "bit-mdl-bl", + BitModalPosition.BottomCenter => "bit-mdl-bc", + BitModalPosition.BottomRight => "bit-mdl-br", + _ => "bit-mdl-ctr" + }); } protected override void RegisterCssStyles() @@ -130,7 +166,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (IsOpen) { - if (IsDraggable) + if (Draggable) { _ = _js.BitModalSetupDragDrop(_containerId, GetDragElementSelector()); } @@ -161,27 +197,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender) private async Task CloseModal(MouseEventArgs e) { if (IsEnabled is false) return; - if (IsBlocking is not false) return; + if (Blocking is not false) return; if (await AssignIsOpen(false) is false) return; await OnDismiss.InvokeAsync(e); } - private string GetPositionClass() => Position switch - { - BitModalPosition.Center => "bit-mdl-ctr", - BitModalPosition.TopLeft => "bit-mdl-tl", - BitModalPosition.TopCenter => "bit-mdl-tc", - BitModalPosition.TopRight => "bit-mdl-tr", - BitModalPosition.CenterLeft => "bit-mdl-cl", - BitModalPosition.CenterRight => "bit-mdl-cr", - BitModalPosition.BottomLeft => "bit-mdl-bl", - BitModalPosition.BottomCenter => "bit-mdl-bc", - BitModalPosition.BottomRight => "bit-mdl-br", - _ => "bit-mdl-ctr", - }; - private string GetDragElementSelector() => DragElementSelector ?? $"#{_containerId}"; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.scss b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.scss index 2c20ec9b0a..370c4664f5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.scss @@ -2,13 +2,21 @@ .bit-mdl { inset: 0; + opacity: 1; width: 100%; height: 100%; + outline: none; + display: flex; position: fixed; font-weight: 400; + align-items: center; + pointer-events: auto; z-index: $zindex-modal; + justify-content: center; font-size: spacing(1.75); font-family: $tg-font-family; + background-color: transparent; + transition: opacity 300ms ease 0s; } .bit-mdl-nta { @@ -24,18 +32,6 @@ position: absolute; } -.bit-mdl-doc { - opacity: 1; - width: 100%; - height: 100%; - display: flex; - outline: none; - position: absolute; - pointer-events: auto; - background-color: transparent; - transition: opacity 300ms ease 0s; -} - .bit-mdl-ovl { inset: 0; z-index: 0; @@ -43,21 +39,25 @@ background-color: $clr-bg-overlay; } -.bit-mdl-scn { - flex-grow: 1; - display: flex; - overflow-y: auto; - max-height: 100vh; - flex-direction: column; -} - .bit-mdl-ctn { max-width: 100%; position: absolute; box-sizing: border-box; + background-color: $clr-bg-pri; box-shadow: $box-shadow-callout; border-radius: $shp-border-radius; - background-color: $clr-bg-pri; +} + +.bit-mdl-fhe { + .bit-mdl-ctn { + height: 100%; + } +} + +.bit-mdl-fwi { + .bit-mdl-ctn { + width: 100%; + } } .bit-mdl-ctr { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalClassStyles.cs index e4cad2f86a..e01158b637 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalClassStyles.cs @@ -7,11 +7,6 @@ public class BitModalClassStyles /// </summary> public string? Root { get; set; } - /// <summary> - /// Custom CSS classes/styles for the main container of the BitModal. - /// </summary> - public string? Container { get; set; } - /// <summary> /// Custom CSS classes/styles for the overlay of the BitModal. /// </summary> @@ -21,9 +16,4 @@ public class BitModalClassStyles /// Custom CSS classes/styles for the modal content of the BitModal. /// </summary> public string? Content { get; set; } - - /// <summary> - /// Custom CSS classes/styles for the scroll content of the BitModal. - /// </summary> - public string? ScrollContent { get; set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs index d542c814be..cc138f461e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs @@ -15,7 +15,7 @@ public partial class BitPanel : BitComponentBase /// <summary> /// Enables the auto scrollbar toggle behavior of the Panel. /// </summary> - [Parameter] public bool AutoToggleScroll { get; set; } = true; + [Parameter] public bool AutoToggleScroll { get; set; } /// <summary> /// The content of the Panel, it can be any custom tag or text. diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs index c534c4e283..4ee326e81c 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs @@ -80,7 +80,7 @@ public partial class BitSplitter : BitComponentBase /// Sets the orientation of BitSplitter to vertical. /// </summary> [Parameter, ResetClassBuilder] - [CallOnSet(nameof(ResetPaneDimensions))] + [CallOnSet(nameof(OnSetVertical))] public bool Vertical { get; set; } @@ -109,7 +109,7 @@ protected override void RegisterCssStyles() - private async Task ResetPaneDimensions() + private async Task OnSetVertical() { await _js.BitSplitterResetPaneDimensions(_firstPanelRef); await _js.BitSplitterResetPaneDimensions(_secondPanelRef); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.razor.cs index 149e0553e8..44140cd07a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.razor.cs @@ -20,6 +20,12 @@ public partial class BitIcon : BitComponentBase [Parameter, ResetClassBuilder] public BitSize? Size { get; set; } + /// <summary> + /// The visual variant of the icon. + /// </summary> + [Parameter, ResetClassBuilder] + public BitVariant? Variant { get; set; } + protected override string RootElementClass => "bit-ico"; @@ -36,6 +42,15 @@ protected override void RegisterCssClasses() BitColor.Warning => "bit-ico-wrn", BitColor.SevereWarning => "bit-ico-swr", BitColor.Error => "bit-ico-err", + BitColor.PrimaryBackground => "bit-ico-pbg", + BitColor.SecondaryBackground => "bit-ico-sbg", + BitColor.TertiaryBackground => "bit-ico-tbg", + BitColor.PrimaryForeground => "bit-ico-pfg", + BitColor.SecondaryForeground => "bit-ico-sfg", + BitColor.TertiaryForeground => "bit-ico-tfg", + BitColor.PrimaryBorder => "bit-ico-pbr", + BitColor.SecondaryBorder => "bit-ico-sbr", + BitColor.TertiaryBorder => "bit-ico-tbr", _ => "bit-ico-pri" }); @@ -48,5 +63,13 @@ protected override void RegisterCssClasses() BitSize.Large => "bit-ico-lg", _ => "bit-ico-md" }); + + ClassBuilder.Register(() => Variant switch + { + BitVariant.Fill => "bit-ico-fil", + BitVariant.Outline => "bit-ico-out", + BitVariant.Text => "bit-ico-txt", + _ => "bit-ico-txt" + }); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss index 4ba51e1692..ad9a4a9583 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss @@ -1,41 +1,121 @@ @import "../../../Styles/functions.scss"; .bit-ico { + padding: spacing(0.5); + border-radius: $shp-border-radius; + border-style: $shp-border-style; + border-width: $shp-border-width; + &.bit-dis { color: $clr-fg-dis; } } + +.bit-ico-fil { + color: var(--bit-ico-clr-txt); + border-color: var(--bit-ico-clr); + background-color: var(--bit-ico-clr); +} + +.bit-ico-out { + color: var(--bit-ico-clr); + border-color: var(--bit-ico-clr); + background-color: transparent; +} + +.bit-ico-txt { + color: var(--bit-ico-clr); + border-color: transparent; + background-color: transparent; + border: none; + padding: 0; +} + + .bit-ico-pri { - color: $clr-pri; + --bit-ico-clr: #{$clr-pri}; + --bit-ico-clr-txt: #{$clr-pri-text}; } .bit-ico-sec { - color: $clr-sec; + --bit-ico-clr: #{$clr-sec}; + --bit-ico-clr-txt: #{$clr-sec-text}; } .bit-ico-ter { - color: $clr-ter; + --bit-ico-clr: #{$clr-ter}; + --bit-ico-clr-txt: #{$clr-ter-text}; } .bit-ico-inf { - color: $clr-inf; + --bit-ico-clr: #{$clr-inf}; + --bit-ico-clr-txt: #{$clr-inf-text}; } .bit-ico-suc { - color: $clr-suc; + --bit-ico-clr: #{$clr-suc}; + --bit-ico-clr-txt: #{$clr-suc-text}; } .bit-ico-wrn { - color: $clr-wrn; + --bit-ico-clr: #{$clr-wrn}; + --bit-ico-clr-txt: #{$clr-wrn-text}; } .bit-ico-swr { - color: $clr-swr; + --bit-ico-clr: #{$clr-swr}; + --bit-ico-clr-txt: #{$clr-swr-text}; } .bit-ico-err { - color: $clr-err; + --bit-ico-clr: #{$clr-err}; + --bit-ico-clr-txt: #{$clr-err-text}; +} + +.bit-ico-pbg { + --bit-ico-clr: #{$clr-bg-pri}; + --bit-ico-clr-txt: #{$clr-fg-pri}; +} + +.bit-ico-sbg { + --bit-ico-clr: #{$clr-bg-sec}; + --bit-ico-clr-txt: #{$clr-fg-sec}; +} + +.bit-ico-tbg { + --bit-ico-clr: #{$clr-bg-ter}; + --bit-ico-clr-txt: #{$clr-fg-ter}; +} + +.bit-ico-pfg { + --bit-ico-clr: #{$clr-fg-pri}; + --bit-ico-clr-txt: #{$clr-bg-pri}; +} + +.bit-ico-sfg { + --bit-ico-clr: #{$clr-fg-sec}; + --bit-ico-clr-txt: #{$clr-bg-sec}; +} + +.bit-ico-tfg { + --bit-ico-clr: #{$clr-fg-ter}; + --bit-ico-clr-txt: #{$clr-bg-ter}; +} + +.bit-ico-pbr { + --bit-ico-clr: #{$clr-bg-pri}; + --bit-ico-clr-txt: #{$clr-fg-pri}; +} + +.bit-ico-sbr { + --bit-ico-clr: #{$clr-bg-sec}; + --bit-ico-clr-txt: #{$clr-fg-sec}; +} + +.bit-ico-tbr { + --bit-ico-clr: #{$clr-bg-ter}; + --bit-ico-clr-txt: #{$clr-fg-ter}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor index e3298bc6bd..afd69d8a74 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor @@ -18,6 +18,7 @@ { <a @ref="RootElement" @attributes="HtmlAttributes" id="@_Id" + rel="@_rel" aria-label="@AriaLabel" style="@StyleBuilder.Value" class="@ClassBuilder.Value" diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor.cs index e15c564976..952d7ee7c7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.razor.cs @@ -2,6 +2,10 @@ public partial class BitLink : BitComponentBase { + private string? _rel; + + + [Inject] private IJSRuntime _js { get; set; } = default!; @@ -14,7 +18,9 @@ public partial class BitLink : BitComponentBase /// <summary> /// URL the link points to. /// </summary> - [Parameter] public string? Href { get; set; } + [Parameter] + [CallOnSet(nameof(OnSetHrefAndRel))] + public string? Href { get; set; } /// <summary> /// Styles the link to have no underline at any state. @@ -27,6 +33,13 @@ public partial class BitLink : BitComponentBase /// </summary> [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; } + /// <summary> + /// If Href provided, specifies the relationship between the current document and the linked document. + /// </summary> + [Parameter] + [CallOnSet(nameof(OnSetHrefAndRel))] + public BitAnchorRel? Rel { get; set; } + /// <summary> /// If Href provided, specifies how to open the link. /// </summary> @@ -62,4 +75,15 @@ private async Task ScrollIntoView() await _js.ScrollElementIntoView(Href![1..]); } + + private void OnSetHrefAndRel() + { + if (Rel.HasValue is false || Href.HasNoValue() || Href!.StartsWith('#')) + { + _rel = null; + return; + } + + _rel = string.Join(" ", Enum.GetValues(typeof(BitAnchorRel)).Cast<BitAnchorRel>().Where(r => Rel.Value.HasFlag(r)).Select(r => r.ToString().ToLower())); + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.razor.cs index 1bf6890e8a..1e0c39f227 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.razor.cs @@ -14,6 +14,18 @@ public partial class BitSeparator : BitComponentBase [Parameter, ResetStyleBuilder] public bool AutoSize { get; set; } + /// <summary> + /// The color kind of the background of the separator. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColorKind? Background { get; set; } + + /// <summary> + /// The color kind of the border of the separator. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColorKind? Border { get; set; } + /// <summary> /// The content of the Separator, it can be any custom tag or text. /// </summary> @@ -31,14 +43,32 @@ public partial class BitSeparator : BitComponentBase protected override void RegisterCssClasses() { - ClassBuilder.Register(() => Vertical ? "bit-spr-vrt" : "bit-spr-hrz"); - ClassBuilder.Register(() => AlignContent switch { BitSeparatorAlignContent.Start => "bit-spr-srt", BitSeparatorAlignContent.End => "bit-spr-end", _ => "bit-spr-ctr" }); + + ClassBuilder.Register(() => Background switch + { + BitColorKind.Primary => "bit-spr-pbg", + BitColorKind.Secondary => "bit-spr-sbg", + BitColorKind.Tertiary => "bit-spr-tbg", + BitColorKind.Transparent => "bit-spr-rbg", + _ => null + }); + + ClassBuilder.Register(() => Border switch + { + BitColorKind.Primary => "bit-spr-pbr", + BitColorKind.Secondary => "bit-spr-sbr", + BitColorKind.Tertiary => "bit-spr-tbr", + BitColorKind.Transparent => "bit-spr-rbr", + _ => null + }); + + ClassBuilder.Register(() => Vertical ? "bit-spr-vrt" : "bit-spr-hrz"); } protected override void RegisterCssStyles() diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.scss b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.scss index 7bd68b5eb7..0f88476ee2 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Separator/BitSeparator.scss @@ -2,13 +2,15 @@ .bit-spr { position: relative; + --bit-spr-bg: #{$clr-bg-pri}; + --bit-spr-brd: #{$clr-brd-sec}; } .bit-spr-cnt { position: relative; color: $clr-fg-pri; display: inline-block; - background: $clr-bg-pri; + background: var(--bit-spr-bg); } .bit-spr-hrz { @@ -22,7 +24,7 @@ inset: 50% 0 0; position: absolute; height: $shp-border-width; - background-color: $clr-brd-sec; + background-color: var(--bit-spr-brd); } .bit-spr-cnt { @@ -55,7 +57,7 @@ inset: 0 0 0 50%; position: absolute; width: $shp-border-width; - background-color: $clr-brd-sec; + background-color: var(--bit-spr-brd); } .bit-spr-cnt { @@ -74,3 +76,37 @@ vertical-align: bottom; } } + + +.bit-spr-pbg { + --bit-spr-bg: #{$clr-bg-pri}; +} + +.bit-spr-sbg { + --bit-spr-bg: #{$clr-bg-sec}; +} + +.bit-spr-tbg { + --bit-spr-bg: #{$clr-bg-ter}; +} + +.bit-spr-rbg { + --bit-spr-bg: transparent; +} + + +.bit-spr-pbr { + --bit-spr-brd: #{$clr-brd-pri}; +} + +.bit-spr-sbr { + --bit-spr-brd: #{$clr-brd-sec}; +} + +.bit-spr-tbr { + --bit-spr-brd: #{$clr-brd-ter}; +} + +.bit-spr-rbr { + --bit-spr-brd: transparent; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.cs index b6d730be4d..dbabd1c3c0 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.cs @@ -5,16 +5,33 @@ namespace Bit.BlazorUI; public partial class BitText : BitComponentBase { + /// <summary> + /// Sets the horizontal alignment of the text content. + /// </summary> + [Parameter] public BitTextAlign? Align { get; set; } + /// <summary> /// The content of the Text. /// </summary> [Parameter] public RenderFragment? ChildContent { get; set; } + /// <summary> + /// The general color of the text. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColor? Color { get; set; } + /// <summary> /// The custom html element used for the root node. /// </summary> [Parameter] public string? Element { get; set; } + /// <summary> + /// The kind of the foreground color of the text. + /// </summary> + [Parameter, ResetClassBuilder] + public BitColorKind? Foreground { get; set; } + /// <summary> /// If true, the text will have a bottom margin. /// </summary> @@ -43,6 +60,59 @@ protected override void RegisterCssClasses() ClassBuilder.Register(() => $"bit-txt-{(Typography ?? BitTypography.Subtitle1).ToString().ToLower(CultureInfo.InvariantCulture)}") .Register(() => NoWrap ? "bit-txt-nowrap" : string.Empty) .Register(() => Gutter ? "bit-txt-gutter" : string.Empty); + + ClassBuilder.Register(() => Color switch + { + BitColor.Primary => "bit-txt-pri", + BitColor.Secondary => "bit-txt-sec", + BitColor.Tertiary => "bit-txt-ter", + BitColor.Info => "bit-txt-inf", + BitColor.Success => "bit-txt-suc", + BitColor.Warning => "bit-txt-wrn", + BitColor.SevereWarning => "bit-txt-swr", + BitColor.Error => "bit-txt-err", + BitColor.PrimaryBackground => "bit-txt-pbg", + BitColor.SecondaryBackground => "bit-txt-sbg", + BitColor.TertiaryBackground => "bit-txt-tbg", + BitColor.PrimaryForeground => "bit-txt-pfg", + BitColor.SecondaryForeground => "bit-txt-sfg", + BitColor.TertiaryForeground => "bit-txt-tfg", + BitColor.PrimaryBorder => "bit-txt-pbr", + BitColor.SecondaryBorder => "bit-txt-sbr", + BitColor.TertiaryBorder => "bit-txt-tbr", + _ => string.Empty + }); + + ClassBuilder.Register(() => Foreground switch + { + BitColorKind.Primary => "bit-txt-pfg", + BitColorKind.Secondary => "bit-txt-sfg", + BitColorKind.Tertiary => "bit-txt-tfg", + BitColorKind.Transparent => "bit-txt-rfg", + _ => string.Empty + }); + } + + protected override void RegisterCssStyles() + { + StyleBuilder.Register(() => Align.HasValue is false ? null : + $"text-align:{Align switch + { + BitTextAlign.Start => "start", + BitTextAlign.End => "end", + BitTextAlign.Left => "left", + BitTextAlign.Right => "right", + BitTextAlign.Center => "center", + BitTextAlign.Justify => "justify", + BitTextAlign.JustifyAll => "justify-all", + BitTextAlign.MatchParent => "match-parent", + BitTextAlign.Inherit => "inherit", + BitTextAlign.Initial => "initial", + BitTextAlign.Revert => "revert", + BitTextAlign.RevertLayer => "revert-layer", + BitTextAlign.Unset => "unset", + _ => "start" + }}"); } protected override void BuildRenderTree(RenderTreeBuilder builder) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.scss b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.scss index 7c3ca023c5..1e0fb40132 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Text/BitText.scss @@ -1,6 +1,7 @@ @import "../../../Styles/functions.scss"; .bit-txt { + color: inherit; font-family: $tg-font-family; } @@ -130,6 +131,79 @@ } +.bit-txt-pri { + color: $clr-pri; +} + +.bit-txt-sec { + color: $clr-sec; +} + +.bit-txt-ter { + color: $clr-ter; +} + +.bit-txt-inf { + color: $clr-inf; +} + +.bit-txt-suc { + color: $clr-suc; +} + +.bit-txt-wrn { + color: $clr-wrn; +} + +.bit-txt-swr { + color: $clr-swr; +} + +.bit-txt-err { + color: $clr-err; +} + +.bit-txt-pbg { + color: $clr-bg-pri; +} + +.bit-txt-sbg { + color: $clr-bg-sec; +} + +.bit-txt-tbg { + color: $clr-bg-ter; +} + +.bit-txt-pfg { + color: $clr-fg-pri; +} + +.bit-txt-sfg { + color: $clr-fg-sec; +} + +.bit-txt-tfg { + color: $clr-fg-ter; +} + +.bit-txt-pbr { + color: $clr-brd-pri; +} + +.bit-txt-sbr { + color: $clr-brd-sec; +} + +.bit-txt-tbr { + color: $clr-brd-ter; +} + +.bit-txt-rfg { + color: transparent; +} + + .bit-txt-nowrap { overflow: hidden; white-space: nowrap; diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index 47244a6012..33ce5a4490 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -68,14 +68,15 @@ public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string ident - public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime) + internal static bool IsRuntimeInvalid(this IJSRuntime jsRuntime) { var type = jsRuntime.GetType(); - if (type.Name is "UnsupportedJavaScriptRuntime") return true; - - if (type.Name is not "RemoteJSRuntime") return false; // Blazor WASM/Hybrid - - return (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)! is false; + return type.Name switch + { + "UnsupportedJavaScriptRuntime" => true, // Prerendering + "RemoteJSRuntime" => (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)! is false, // Blazor server + _ => false // Blazor WASM/Hybrid + }; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts index db93a419ed..bc021a8c6d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts +++ b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts @@ -1,4 +1,4 @@ -(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '8.11.0'; +(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '8.12.0'; interface DotNetObject { invokeMethod<T>(methodIdentifier: string, ...args: any[]): T; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss index e5f62b17c3..60f6a3bb68 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss @@ -99,12 +99,12 @@ --bit-clr-fg-ter: #616973; --bit-clr-fg-ter-hover: #565D66; --bit-clr-fg-ter-active: #4B5159; - --bit-clr-fg-dis: #444; + --bit-clr-fg-dis: #343b42; //backgrounds --bit-clr-bg-pri: #010409; - --bit-clr-bg-pri-hover: #101419; - --bit-clr-bg-pri-active: #14191F; - --bit-clr-bg-sec: #161B22; + --bit-clr-bg-pri-hover: #0E141B; + --bit-clr-bg-pri-active: #0C141D; + --bit-clr-bg-sec: #101419; --bit-clr-bg-sec-hover: #1C222B; --bit-clr-bg-sec-active: #202731; --bit-clr-bg-ter: #181F26; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss index aa8cbb1148..a06f610841 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss @@ -104,8 +104,8 @@ --bit-clr-fg-dis: #AAA; //backgrounds --bit-clr-bg-pri: #FFF; - --bit-clr-bg-pri-hover: #FAF9F8; - --bit-clr-bg-pri-active: #F2F1F0; + --bit-clr-bg-pri-hover: #F2F2F2; + --bit-clr-bg-pri-active: #EBEBEB; --bit-clr-bg-sec: #FAF9F8; --bit-clr-bg-sec-hover: #F2F1F0; --bit-clr-bg-sec-active: #E8E7E6; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/fabric.mdl2.bit.blazoui.scss b/src/BlazorUI/Bit.BlazorUI/Styles/fabric.mdl2.bit.blazoui.scss index 7fe7ea580e..7320c60d99 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/fabric.mdl2.bit.blazoui.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/fabric.mdl2.bit.blazoui.scss @@ -6,7 +6,6 @@ src: url('../fonts/FabMDL2.4.66.bit.BlazorUI.woff2') format("woff2"); } - .bit-icon { font-style: normal; font-weight: normal; @@ -16,14 +15,10 @@ .bit-icon--Accept:before { content: "\E8FB"; } .bit-icon--Add:before { content: "\E710"; } -.bit-icon--Blocked2:before { content: "\ECE4"; } .bit-icon--CalendarMirrored:before { content: "\ED28"; } .bit-icon--Cancel:before { content: "\E711"; } .bit-icon--ChevronDown:before { content: "\E70D"; } .bit-icon--ChevronDownSmall:before { content: "\E96E"; } -.bit-icon--ChevronLeft:before { content: "\E76B"; } -.bit-icon--ChevronLeftEnd6:before { content: "\F371"; } -.bit-icon--ChevronLeftSmall:before { content: "\E96F"; } .bit-icon--ChevronRight:before { content: "\E76C"; } .bit-icon--ChevronUpSmall:before { content: "\E96D"; } .bit-icon--ChromeBackMirrored:before { content: "\EA47"; } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/general.scss b/src/BlazorUI/Bit.BlazorUI/Styles/general.scss index 7cf07c8acf..a85492c33b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/general.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/general.scss @@ -30,6 +30,14 @@ button { position: absolute; } +.bit-ico-r90 { + transform: rotate(90deg); +} + +.bit-ico-r180 { + transform: rotate(180deg); +} + @keyframes bit-fade-show { 0% { opacity: 0; diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme/BitThemeColors.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme/BitThemeColors.cs index 040e126c76..64b3b9bb80 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme/BitThemeColors.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme/BitThemeColors.cs @@ -39,6 +39,9 @@ public class BitThemeGeneralColorVariants public string? Secondary { get; set; } public string? SecondaryHover { get; set; } public string? SecondaryActive { get; set; } + public string? Tertiary { get; set; } + public string? TertiaryHover { get; set; } + public string? TertiaryActive { get; set; } public string? Disabled { get; set; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeMapper.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeMapper.cs index 3331f60cce..4abe411574 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeMapper.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeMapper.cs @@ -102,6 +102,9 @@ internal static Dictionary<string, string> MapToCssVariables(BitTheme bitTheme) addCssVar("--bit-clr-fg-sec", bitTheme.Color.Foreground.Secondary); addCssVar("--bit-clr-fg-sec-hover", bitTheme.Color.Foreground.SecondaryHover); addCssVar("--bit-clr-fg-sec-active", bitTheme.Color.Foreground.SecondaryActive); + addCssVar("--bit-clr-fg-ter", bitTheme.Color.Foreground.Tertiary); + addCssVar("--bit-clr-fg-ter-hover", bitTheme.Color.Foreground.TertiaryHover); + addCssVar("--bit-clr-fg-ter-active", bitTheme.Color.Foreground.TertiaryActive); addCssVar("--bit-clr-fg-dis", bitTheme.Color.Foreground.Disabled); addCssVar("--bit-clr-bg-pri", bitTheme.Color.Background.Primary); @@ -110,6 +113,9 @@ internal static Dictionary<string, string> MapToCssVariables(BitTheme bitTheme) addCssVar("--bit-clr-bg-sec", bitTheme.Color.Background.Secondary); addCssVar("--bit-clr-bg-sec-hover", bitTheme.Color.Background.SecondaryHover); addCssVar("--bit-clr-bg-sec-active", bitTheme.Color.Background.SecondaryActive); + addCssVar("--bit-clr-bg-ter", bitTheme.Color.Background.Tertiary); + addCssVar("--bit-clr-bg-ter-hover", bitTheme.Color.Background.TertiaryHover); + addCssVar("--bit-clr-bg-ter-active", bitTheme.Color.Background.TertiaryActive); addCssVar("--bit-clr-bg-dis", bitTheme.Color.Background.Disabled); addCssVar("--bit-clr-bg-overlay", bitTheme.Color.Background.Overlay); @@ -119,6 +125,9 @@ internal static Dictionary<string, string> MapToCssVariables(BitTheme bitTheme) addCssVar("--bit-clr-brd-sec", bitTheme.Color.Border.Secondary); addCssVar("--bit-clr-brd-sec-hover", bitTheme.Color.Border.SecondaryHover); addCssVar("--bit-clr-brd-sec-active", bitTheme.Color.Border.SecondaryActive); + addCssVar("--bit-clr-brd-ter", bitTheme.Color.Border.Tertiary); + addCssVar("--bit-clr-brd-ter-hover", bitTheme.Color.Border.TertiaryHover); + addCssVar("--bit-clr-brd-ter-active", bitTheme.Color.Border.TertiaryActive); addCssVar("--bit-clr-brd-dis", bitTheme.Color.Border.Disabled); addCssVar("--bit-clr-req", bitTheme.Color.Required); @@ -395,6 +404,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Foreground.Secondary = bitTheme.Color.Foreground.Secondary ?? other.Color.Foreground.Secondary; result.Color.Foreground.SecondaryHover = bitTheme.Color.Foreground.SecondaryHover ?? other.Color.Foreground.SecondaryHover; result.Color.Foreground.SecondaryActive = bitTheme.Color.Foreground.SecondaryActive ?? other.Color.Foreground.SecondaryActive; + result.Color.Foreground.Tertiary = bitTheme.Color.Foreground.Tertiary ?? other.Color.Foreground.Tertiary; + result.Color.Foreground.TertiaryHover = bitTheme.Color.Foreground.TertiaryHover ?? other.Color.Foreground.TertiaryHover; + result.Color.Foreground.TertiaryActive = bitTheme.Color.Foreground.TertiaryActive ?? other.Color.Foreground.TertiaryActive; result.Color.Foreground.Disabled = bitTheme.Color.Foreground.Disabled ?? other.Color.Foreground.Disabled; result.Color.Background.Primary = bitTheme.Color.Background.Primary ?? other.Color.Background.Primary; @@ -403,6 +415,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Background.Secondary = bitTheme.Color.Background.Secondary ?? other.Color.Background.Secondary; result.Color.Background.SecondaryHover = bitTheme.Color.Background.SecondaryHover ?? other.Color.Background.SecondaryHover; result.Color.Background.SecondaryActive = bitTheme.Color.Background.SecondaryActive ?? other.Color.Background.SecondaryActive; + result.Color.Background.Tertiary = bitTheme.Color.Background.Tertiary ?? other.Color.Background.Tertiary; + result.Color.Background.TertiaryHover = bitTheme.Color.Background.TertiaryHover ?? other.Color.Background.TertiaryHover; + result.Color.Background.TertiaryActive = bitTheme.Color.Background.TertiaryActive ?? other.Color.Background.TertiaryActive; result.Color.Background.Disabled = bitTheme.Color.Background.Disabled ?? other.Color.Background.Disabled; result.Color.Background.Overlay = bitTheme.Color.Background.Overlay ?? other.Color.Background.Overlay; @@ -412,6 +427,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Border.Secondary = bitTheme.Color.Border.Secondary ?? other.Color.Border.Secondary; result.Color.Border.SecondaryHover = bitTheme.Color.Border.SecondaryHover ?? other.Color.Border.SecondaryHover; result.Color.Border.SecondaryActive = bitTheme.Color.Border.SecondaryActive ?? other.Color.Border.SecondaryActive; + result.Color.Border.Tertiary = bitTheme.Color.Border.Tertiary ?? other.Color.Border.Tertiary; + result.Color.Border.TertiaryHover = bitTheme.Color.Border.TertiaryHover ?? other.Color.Border.TertiaryHover; + result.Color.Border.TertiaryActive = bitTheme.Color.Border.TertiaryActive ?? other.Color.Border.TertiaryActive; result.Color.Border.Disabled = bitTheme.Color.Border.Disabled ?? other.Color.Border.Disabled; result.Color.Required = bitTheme.Color.Required ?? other.Color.Required; diff --git a/src/BlazorUI/Bit.BlazorUI/package-lock.json b/src/BlazorUI/Bit.BlazorUI/package-lock.json index 9df2c8fef3..1aa9f752e8 100644 --- a/src/BlazorUI/Bit.BlazorUI/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/BlazorUI/Bit.BlazorUI/package.json b/src/BlazorUI/Bit.BlazorUI/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/BlazorUI/Bit.BlazorUI/package.json +++ b/src/BlazorUI/Bit.BlazorUI/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Bit.BlazorUI/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.woff2 b/src/BlazorUI/Bit.BlazorUI/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.woff2 index bf161658eb..45daf4f8ab 100644 Binary files a/src/BlazorUI/Bit.BlazorUI/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.woff2 and b/src/BlazorUI/Bit.BlazorUI/wwwroot/fonts/FabMDL2.4.66.bit.BlazorUI.woff2 differ diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 596bab276e..df65f045da 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -1,27 +1,27 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.AspNetCore.OData" Version="9.0.0" /> <PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" /> <PackageReference Include="AspNetCore.HealthChecks.System" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> </ItemGroup> <ItemGroup> diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index b7c237cbc2..6858b979df 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -36,7 +36,7 @@ gtag('config', 'G-G1ET5L69QF'); </Script> <!-- Google tag --> - <!-- Microsoft Clarity --> + <!-- Microsoft Clarity <Script> (function (c, l, a, r, i, t, y) { c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments) }; @@ -44,13 +44,14 @@ y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y); })(window, document, "clarity", "script", "ipec21ezsa"); </Script> - <!-- Microsoft Clarity --> + Microsoft Clarity --> <Link rel="stylesheet" href="_content/Bit.BlazorUI/styles/bit.blazorui.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Icons/styles/bit.blazorui.icons.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/styles/app.css" /> + <Link rel="stylesheet" href="Bit.BlazorUI.Demo.Server.styles.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.bundle.scp.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/prism-1.28.0/prism-okaidia-bit.css" /> @@ -62,7 +63,7 @@ @if (HttpContext.Request.IsCrawlerClient() is false) { - <Script src="_framework/blazor.web.js" autostart="false"></Script> + <Script src="_framework/blazor.web.js?v=9.0.0-rc.2.24474.3" autostart="false"></Script> @if (AppRenderMode.PwaEnabled) { <Script src="_content/Bit.Bswup/bit-bswup.js"></Script> diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Startup/Middlewares.cs b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Startup/Middlewares.cs index 6e9e18e251..907a30c9fd 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Startup/Middlewares.cs +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Startup/Middlewares.cs @@ -3,6 +3,7 @@ using System.Runtime.Loader; using Bit.BlazorUI.Demo.Server.Components; using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Endpoints; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http.Extensions; @@ -48,7 +49,7 @@ public static void Use(WebApplication app, IWebHostEnvironment env, IConfigurati } app.UseStaticFiles(); - app.UseCors(options => options.WithOrigins("https://0.0.0.0" /*BlazorHybrid*/, "app://0.0.0.0" /*BlazorHybrid*/) + app.UseCors(options => options.WithOrigins("https://0.0.0.0", "https://0.0.0.1" /*BlazorHybrid*/, "app://0.0.0.0", "app://0.0.0.1" /*BlazorHybrid*/) .AllowAnyHeader().AllowAnyMethod()); app.UseResponseCaching(); @@ -81,6 +82,8 @@ public static void Use(WebApplication app, IWebHostEnvironment env, IConfigurati }); } + UseSiteMap(app); + // Handle the rest of requests with blazor app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() @@ -140,4 +143,32 @@ private static void Configure_401_403_404_Pages(WebApplication app) } }); } + + private static void UseSiteMap(WebApplication app) + { + var urls = Assembly.Load("Bit.BlazorUI.Demo.Client.Core") + .ExportedTypes + .Where(t => typeof(IComponent).IsAssignableFrom(t)) + .SelectMany(t => t.GetCustomAttributes<Microsoft.AspNetCore.Components.RouteAttribute>()) + .Select(r => r.Template) + .ToList(); + + const string siteMapHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset\r\n xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\r\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"; + + app.MapGet("/sitemap.xml", async context => + { + if (siteMap is null) + { + var baseUrl = context.Request.GetBaseUrl(); + + siteMap = $"{siteMapHeader}{string.Join(Environment.NewLine, urls.Select(u => $"<url><loc>{new Uri(baseUrl, u)}</loc></url>"))}</urlset>"; + } + + context.Response.Headers.ContentType = "application/xml"; + + await context.Response.WriteAsync(siteMap, context.RequestAborted); + }); + } + + private static string? siteMap; } diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj index 34253a8973..1959d70b2f 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj @@ -5,19 +5,19 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> - <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.8" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> + <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.10" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> - <PackageReference Include="System.Text.Json" Version="8.0.4" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" PrivateAssets="all" ExcludeAssets="runtime"> + <PackageReference Include="System.Text.Json" Version="8.0.5" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" PrivateAssets="all" ExcludeAssets="runtime"> <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj index e40a49962d..1c25d95b4c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj @@ -16,17 +16,17 @@ <Content Remove="appsettings.json" /> <EmbeddedResource Include="appsettings.json" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.10" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" /> <Using Include="System.Net.Http.Json" /> <Using Include="System.Collections.Concurrent" /> @@ -88,11 +88,11 @@ </Target> <Target Name="BuildIsolatedScssFiles" Inputs="@(IsolatedScssFiles)" Outputs="@(IsolatedScssFiles->Replace('.scss', '.css'))"> - <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <Target Name="BuildGlobalScssFiles" Inputs="@(GlobalScssFiles)" Outputs="wwwroot/styles/app.css"> - <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/ComponentDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/ComponentDemo.razor.cs index cc67574b63..a2ab158895 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/ComponentDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/ComponentDemo.razor.cs @@ -242,6 +242,20 @@ public partial class ComponentDemo private readonly List<ComponentParameter> _textInputBaseParameters = [ + new() + { + Name = "AutoComplete", + Type = "string?", + DefaultValue = "null", + Description = "Specifies the value of the autocomplete attribute of the input component.", + }, + new() + { + Name = "AutoFocus", + Type = "bool", + DefaultValue = "false", + Description = "Determines if the text input is auto focused on first render.", + }, new() { Name = "DebounceTime", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor index 9d1095dad2..74944fa146 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor @@ -83,7 +83,7 @@ <BitActionButton Color="BitColor.Error">Error</BitActionButton> </div> <br /><br /> - <div style="background:var(--bit-clr-fg-sec);padding:1rem"> + <div style="background:var(--bit-clr-fg-ter);padding:1rem"> <div class="example-content"> <BitActionButton Color="BitColor.PrimaryBackground" IconName="@BitIconName.ColorSolid">PrimaryBackground</BitActionButton> <BitActionButton Color="BitColor.PrimaryBackground">PrimaryBackground</BitActionButton> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor.cs index 94a9c85480..21ac7dcb05 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/BitActionButtonDemo.razor.cs @@ -60,6 +60,13 @@ public partial class BitActionButtonDemo Href = "#color-enum", }, new() + { + Name = "FullWidth", + Type = "bool", + DefaultValue = "false", + Description = "Renders the action button in full width of its container with flex-start.", + }, + new() { Name = "Href", Type = "string?", @@ -74,6 +81,13 @@ public partial class BitActionButtonDemo Description = "The icon name of the icon to render inside the button.", }, new() + { + Name = "IconOnly", + Type = "bool", + DefaultValue = "null", + Description = "Removes the container of the text and only renders the icon.", + }, + new() { Name = "OnClick", Type = "EventCallback<MouseEventArgs>", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/ButtonGroupActionItem.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/Operation.cs similarity index 75% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/ButtonGroupActionItem.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/Operation.cs index 2fe259a8a3..688e918c52 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/ButtonGroupActionItem.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/Operation.cs @@ -1,6 +1,6 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.ButtonGroup; -public class ButtonGroupActionItem +public class Operation { public string? Id { get; set; } @@ -14,5 +14,5 @@ public class ButtonGroupActionItem public string? Style { get; set; } - public Action<ButtonGroupActionItem>? Clicked { get; set; } + public Action<Operation>? Clicked { get; set; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor index 74ae39aaae..b1fa55899b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor @@ -265,7 +265,7 @@ <div>Component's ItemClick event:</div> <BitButtonGroup Items="basicCustoms" NameSelectors="nameSelector" - OnItemClick="(ButtonGroupActionItem item) => clickedCustom = item.Name" /> + OnItemClick="(Operation item) => clickedCustom = item.Name" /> <div>Clicked item: <b>@clickedCustom</b></div> </div> <br /><br /> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.cs index 1416a281fd..92a70d4085 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.cs @@ -5,33 +5,33 @@ public partial class _BitButtonGroupCustomDemo private int clickCounter; private string? clickedCustom; - private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; + private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; - private List<ButtonGroupActionItem> basicCustoms = + private List<Operation> basicCustoms = [ new() { Name = "Add" }, new() { Name = "Edit" }, new() { Name = "Delete" } ]; - private List<ButtonGroupActionItem> disabledCustoms = + private List<Operation> disabledCustoms = [ new() { Name = "Add" }, new() { Name = "Edit", IsEnabled = false }, new() { Name = "Delete" } ]; - private List<ButtonGroupActionItem> iconCustoms = + private List<Operation> iconCustoms = [ new() { Name = "Add", Icon = BitIconName.Add }, new() { Name = "Edit", Icon = BitIconName.Edit }, new() { Name = "Delete", Icon = BitIconName.Delete } ]; - private List<ButtonGroupActionItem> eventsCustoms = + private List<Operation> eventsCustoms = [ new() { Name = "Increase", Icon = BitIconName.Add }, new() { Name = "Reset", Icon = BitIconName.Reset }, new() { Name = "Decrease", Icon = BitIconName.Remove } ]; - private List<ButtonGroupActionItem> styleClassCustoms = + private List<Operation> styleClassCustoms = [ new() { @@ -47,7 +47,7 @@ public partial class _BitButtonGroupCustomDemo } ]; - private List<ButtonGroupActionItem> rtlCustoms = + private List<Operation> rtlCustoms = [ new() { Name = "اضافه کردن", Icon = BitIconName.Add }, new() { Name = "ویرایش", Icon = BitIconName.Edit }, @@ -66,14 +66,14 @@ protected override void OnInitialized() private readonly string example1RazorCode = @" <BitButtonGroup Items=""basicCustoms"" NameSelectors=""nameSelector"" />"; private readonly string example1CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } };"; @@ -91,20 +91,20 @@ public class ButtonGroupActionItem <BitButtonGroup Variant=""BitVariant.Text"" Items=""disabledCustoms""NameSelectors=""nameSelector"" /> <BitButtonGroup Variant=""BitVariant.Text"" Items=""basicCustoms"" NameSelectors=""nameSelector"" IsEnabled=""false"" />"; private readonly string example2CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } public bool IsEnabled { get; set; } = true; } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } }; -private List<ButtonGroupActionItem> disabledCustoms = new() +private List<Operation> disabledCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"", IsEnabled = false }, new() { Name = ""Delete"" } };"; @@ -181,14 +181,14 @@ public class ButtonGroupActionItem <BitButtonGroup Color=""BitColor.TertiaryBorder"" Variant=""BitVariant.Outline"" Items=""basicCustoms"" NameSelectors=""nameSelector"" /> <BitButtonGroup Color=""BitColor.TertiaryBorder"" Variant=""BitVariant.Text"" Items=""basicCustoms"" NameSelectors=""nameSelector"" />"; private readonly string example3CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } };"; @@ -206,13 +206,13 @@ public class ButtonGroupActionItem NameSelectors=""@(new() { Text = { Selector = i => i.Name }, IconName = { Selector = i => i.Icon } })"" />"; private readonly string example4CsharpCode = @" -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } public string? Icon { get; set; } } -private List<ButtonGroupActionItem> iconCustoms = new() +private List<Operation> iconCustoms = new() { new() { Name = ""Add"", Icon = BitIconName.Add }, new() { Name = ""Edit"", Icon = BitIconName.Edit }, @@ -224,14 +224,14 @@ public class ButtonGroupActionItem <BitButtonGroup Variant=""BitVariant.Outline"" Items=""basicCustoms"" NameSelectors=""nameSelector"" Vertical /> <BitButtonGroup Variant=""BitVariant.Text"" Items=""basicCustoms"" NameSelectors=""nameSelector"" Vertical />"; private readonly string example5CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } };"; @@ -249,14 +249,14 @@ public class ButtonGroupActionItem <BitButtonGroup Size=""BitSize.Large"" Variant=""BitVariant.Outline"" Items=""basicCustoms"" NameSelectors=""nameSelector"" /> <BitButtonGroup Size=""BitSize.Large"" Variant=""BitVariant.Text"" Items=""basicCustoms"" NameSelectors=""nameSelector"" />"; private readonly string example6CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } };"; @@ -295,9 +295,9 @@ public class ButtonGroupActionItem NameSelectors=""@(new() { Text = { Selector = i => i.Name }, IconName = { Selector = i => i.Icon } })"" />"; private readonly string example7CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } public string? Icon { get; set; } @@ -305,12 +305,12 @@ public class ButtonGroupActionItem public string? Style { get; set; } } -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } }; -private List<ButtonGroupActionItem> styleClassCustoms = new() +private List<Operation> styleClassCustoms = new() { new() { @@ -329,7 +329,7 @@ public class ButtonGroupActionItem private readonly string example8RazorCode = @" <BitButtonGroup Items=""basicCustoms"" NameSelectors=""nameSelector"" - OnItemClick=""(ButtonGroupActionItem item) => clickedCustom = item.Name"" /> + OnItemClick=""(Operation item) => clickedCustom = item.Name"" /> <div>Clicked item: <b>@clickedCustom</b></div> <BitButtonGroup Items=""eventsCustoms"" @@ -338,23 +338,23 @@ public class ButtonGroupActionItem OnClick = { Selector = i => i.Clicked } })"" /> <div>Click count: <b>@clickCounter</b></div>"; private readonly string example8CsharpCode = @" -private BitButtonGroupNameSelectors<ButtonGroupActionItem> nameSelector = new() { Text = { Selector = i => i.Name } }; +private BitButtonGroupNameSelectors<Operation> nameSelector = new() { Text = { Selector = i => i.Name } }; -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } public string? Icon { get; set; } - public Action<ButtonGroupActionItem>? Clicked { get; set; } + public Action<Operation>? Clicked { get; set; } } private int clickCounter; -private List<ButtonGroupActionItem> basicCustoms = new() +private List<Operation> basicCustoms = new() { new() { Name = ""Add"" }, new() { Name = ""Edit"" }, new() { Name = ""Delete"" } }; -private List<ButtonGroupActionItem> eventsCustoms = new() +private List<Operation> eventsCustoms = new() { new() { Name = ""Increase"", Icon = BitIconName.Add }, new() { Name = ""Reset"", Icon = BitIconName.Reset }, @@ -387,13 +387,13 @@ protected override void OnInitialized() NameSelectors=""@(new() { Text = { Selector = i => i.Name }, IconName = { Selector = i => i.Icon } })"" />"; private readonly string example9CsharpCode = @" -public class ButtonGroupActionItem +public class Operation { public string? Name { get; set; } public string? Icon { get; set; } } -private List<ButtonGroupActionItem> rtlCustoms = new() +private List<Operation> rtlCustoms = new() { new() { Name = ""اضافه کردن"", Icon = BitIconName.Add }, new() { Name = ""ویرایش"", Icon = BitIconName.Edit }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/BitMenuButtonDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/BitMenuButtonDemo.razor.cs index 8fe9d491a4..e78d0e9d42 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/BitMenuButtonDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/BitMenuButtonDemo.razor.cs @@ -30,8 +30,8 @@ public partial class BitMenuButtonDemo new() { Name = "ChevronDownIcon", - Type = "string", - DefaultValue = "ChevronDown", + Type = "string?", + DefaultValue = "null", Description = "The icon name of the chevron down part of the menu button.", }, new() @@ -378,6 +378,13 @@ public partial class BitMenuButtonDemo Name = "Callout", Type = "string?", DefaultValue = "null", + Description = "Custom CSS classes/styles for the callout of the BitMenuButton." + }, + new() + { + Name = "CalloutContainer", + Type = "string?", + DefaultValue = "null", Description = "Custom CSS classes/styles for the callout container of the BitMenuButton." }, new() @@ -409,6 +416,13 @@ public partial class BitMenuButtonDemo Description = "Custom CSS classes/styles for the icon of the BitMenuButton." }, new() + { + Name = "ItemWrapper", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each item wrapper of the BitMenuButton." + }, + new() { Name = "ItemButton", Type = "string?", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/MenuActionItem.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/Operation.cs similarity index 68% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/MenuActionItem.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/Operation.cs index c4899bc180..522f2a8f5c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/MenuActionItem.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/Operation.cs @@ -1,6 +1,6 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.MenuButton; -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -10,7 +10,7 @@ public class MenuActionItem public string? Class { get; set; } public string? Style { get; set; } - public RenderFragment<MenuActionItem>? Fragment { get; set; } + public RenderFragment<Operation>? Fragment { get; set; } - public Action<MenuActionItem>? Clicked { get; set; } + public Action<Operation>? Clicked { get; set; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor index 5f8751f3c6..d61c4178cf 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor @@ -308,8 +308,8 @@ <div>Present BitMenuButton instances with customized icons, enriching the visual options for representing menu buttons.</div> <br /> <div class="example-content"> - <BitMenuButton Text="IconName" Items="basicCustoms" NameSelectors="nameSelectors" IconName="@BitIconName.Edit" /> - <BitMenuButton Text="ChevronDownIcon" Items="basicCustoms" NameSelectors="nameSelectors" ChevronDownIcon="@BitIconName.DoubleChevronDown" Split /> + <BitMenuButton Text="IconName" Items="basicIconCustoms" NameSelectors="nameSelectors" IconName="@BitIconName.Edit" /> + <BitMenuButton Text="ChevronDownIcon" Items="basicIconCustoms" NameSelectors="nameSelectors" ChevronDownIcon="@BitIconName.DoubleChevronDown" Split /> </div> </ExamplePreview> </ComponentExampleBox> @@ -386,23 +386,23 @@ <div>Non-Sticky</div> <div class="example-content"> <BitMenuButton Text="Customs" Items="basicCustoms" NameSelectors="nameSelectors" - OnChange="(MenuActionItem item) => eventsChangedCustom = item?.Id" - OnClick="(MenuActionItem item) => eventsClickedCustom = item?.Id" /> + OnChange="(Operation item) => eventsChangedCustom = item?.Id" + OnClick="(Operation item) => eventsClickedCustom = item?.Id" /> <BitMenuButton Split Text="Customs" Items="basicCustomsOnClick" NameSelectors="nameSelectors" - OnChange="(MenuActionItem item) => eventsChangedCustom = item?.Id" - OnClick="@((MenuActionItem item) => eventsClickedCustom = "Main button clicked")" /> + OnChange="(Operation item) => eventsChangedCustom = item?.Id" + OnClick="@((Operation item) => eventsClickedCustom = "Main button clicked")" /> </div> <br /><br /> <div>Sticky</div> <div class="example-content"> <BitMenuButton Sticky Items="basicCustoms" NameSelectors="nameSelectors" - OnChange="(MenuActionItem item) => eventsChangedCustom = item?.Id" - OnClick="(MenuActionItem item) => eventsClickedCustom = item?.Id" /> + OnChange="(Operation item) => eventsChangedCustom = item?.Id" + OnClick="(Operation item) => eventsClickedCustom = item?.Id" /> <BitMenuButton Sticky Split Items="basicCustomsOnClick" NameSelectors="nameSelectors" - OnChange="(MenuActionItem item) => eventsChangedCustom = item?.Id" - OnClick="(MenuActionItem item) => eventsClickedCustom = item?.Id" /> + OnChange="(Operation item) => eventsChangedCustom = item?.Id" + OnClick="(Operation item) => eventsClickedCustom = item?.Id" /> </div> </div> <br /><br /> @@ -460,19 +460,19 @@ </ComponentExampleBox> @code { - private List<MenuActionItem> itemTemplateCustoms2 = + private List<Operation> itemTemplateCustoms2 = [ new() { Name = "Add", Id = "add-key", Icon = BitIconName.Add, Fragment = (item => @<div class="item-template-box" style="color:green">@item.Name (@item.Id)</div>) }, - new() + new() { Name = "Edit", Id = "edit-key", Icon = BitIconName.Edit, Fragment = (item => @<div class="item-template-box" style="color:yellow">@item.Name (@item.Id)</div>) }, - new() + new() { Name = "Delete", Id = "delete-key", Icon = BitIconName.Delete, Fragment = (item => @<div class="item-template-box" style="color:red">@item.Name (@item.Id)</div>) diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.cs index 94cd0fb269..602ff3faef 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.cs @@ -5,78 +5,78 @@ public partial class _BitMenuButtonCustomDemo private string? eventsChangedCustom; private string? eventsClickedCustom; - private MenuActionItem twoWaySelectedCustom = default!; + private Operation twoWaySelectedCustom = default!; private bool oneWayIsOpen; private bool twoWayIsOpen; - private static BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() + private static BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false }, - Template = { Name = nameof(MenuActionItem.Fragment) } + Template = { Name = nameof(Operation.Fragment) } }; - private static List<MenuActionItem> basicCustoms = + private static List<Operation> basicCustoms = [ new() { Name = "Custom A", Id = "A" }, new() { Name = "Custom B", Id = "B", Disabled = true }, new() { Name = "Custom C", Id = "C" } ]; - private static List<MenuActionItem> basicIconCustoms = + private static List<Operation> basicIconCustoms = [ new() { Name = "Custom A", Id = "A", Icon = BitIconName.Emoji }, new() { Name = "Custom B", Id = "B", Icon = BitIconName.Emoji }, new() { Name = "Custom C", Id = "C", Icon = BitIconName.Emoji2 } ]; - private static List<MenuActionItem> basicCustomsOnClick = + private static List<Operation> basicCustomsOnClick = [ new() { Name = "Custom A", Id = "A", Icon = BitIconName.Emoji }, new() { Name = "Custom B", Id = "B", Icon = BitIconName.Emoji }, new() { Name = "Custom C", Id = "C", Icon = BitIconName.Emoji2 } ]; - private static List<MenuActionItem> itemTemplateCustoms = + private static List<Operation> itemTemplateCustoms = [ new() { Name = "Add", Id = "add-key", Icon = BitIconName.Add }, new() { Name = "Edit", Id = "edit-key", Icon = BitIconName.Edit }, new() { Name = "Delete", Id = "delete-key", Icon = BitIconName.Delete } ]; - private static List<MenuActionItem> itemStyleClassCustoms = + private static List<Operation> itemStyleClassCustoms = [ new() { Name = "Custom A (Default)", Id = "A", Icon = BitIconName.Emoji }, new() { Name = "Custom B (Styled)", Id = "B", Icon = BitIconName.Emoji, Style = "color: tomato; border-color: brown; background-color: peachpuff;" }, new() { Name = "Custom C (Classed)", Id = "C", Icon = BitIconName.Emoji2, Class = "custom-item" }, ]; - private static List<MenuActionItem> isSelectedCustoms = + private static List<Operation> isSelectedCustoms = [ new() { Name = "Custom A", Id = "A", Icon = BitIconName.Emoji }, new() { Name = "Custom B", Id = "B", Icon = BitIconName.Emoji }, new() { Name = "Custom C", Id = "C", Icon = BitIconName.Emoji2, IsSelected = true } ]; - private static List<MenuActionItem> rtlCustoms = + private static List<Operation> rtlCustoms = [ new() { Name = "گزینه الف", Id = "A", Icon = BitIconName.Emoji }, new() { Name = "گزینه ب", Id = "B", Icon = BitIconName.Emoji }, new() { Name = "گزینه ج", Id = "C", Icon = BitIconName.Emoji2 } ]; - private static IEnumerable<BitChoiceGroupItem<MenuActionItem>> choiceGroupCustoms = - basicCustoms.Select(i => new BitChoiceGroupItem<MenuActionItem>() { Id = i.Id, Text = i.Name, IsEnabled = i.Disabled is false, Value = i }); + private static IEnumerable<BitChoiceGroupItem<Operation>> choiceGroupCustoms = + basicCustoms.Select(i => new BitChoiceGroupItem<Operation>() { Id = i.Id, Text = i.Name, IsEnabled = i.Disabled is false, Value = i }); protected override void OnInitialized() { twoWaySelectedCustom = basicCustoms[2]; - Action<MenuActionItem> onClick = item => + Action<Operation> onClick = item => { eventsClickedCustom = $"{item.Name}"; StateHasChanged(); diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs index 386e8289ed..e451f5c5ca 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs @@ -5,7 +5,7 @@ public partial class _BitMenuButtonCustomDemo private readonly string example1RazorCode = @" <BitMenuButton Text=""MenuButton"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example1CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -13,25 +13,25 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; private readonly string example2RazorCode = @" <BitMenuButton Text=""Split"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Split />"; private readonly string example2CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -39,18 +39,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -71,7 +71,7 @@ public class MenuActionItem <BitMenuButton Text=""Outline"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Outline"" IsEnabled=""false"" Split /> <BitMenuButton Text=""Text"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" IsEnabled=""false"" Split />"; private readonly string example3CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -79,18 +79,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -247,7 +247,7 @@ public class MenuActionItem <BitMenuButton Text=""TertiaryBorder"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Outline"" Color=""BitColor.TertiaryBorder"" Split /> <BitMenuButton Text=""TertiaryBorder"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" Color=""BitColor.TertiaryBorder"" Split />"; private readonly string example4CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -255,18 +255,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -283,7 +283,7 @@ public class MenuActionItem <BitMenuButton Text=""Large"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Outline"" Size=""BitSize.Large"" /> <BitMenuButton Text=""Large"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" Size=""BitSize.Large"" />"; private readonly string example5CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -291,18 +291,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -316,7 +316,7 @@ public class MenuActionItem <BitMenuButton Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" Sticky /> <BitMenuButton Items=""basicCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" Split Sticky />"; private readonly string example6CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -324,18 +324,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -343,7 +343,7 @@ public class MenuActionItem <BitMenuButton Text=""IconName"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" IconName=""@BitIconName.Edit"" /> <BitMenuButton Text=""ChevronDownIcon"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" ChevronDownIcon=""@BitIconName.DoubleChevronDown"" Split />"; private readonly string example7CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -351,18 +351,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private static List<Operation> basicIconCustoms = [ - new() { Name = ""Custom A"", Id = ""A"" }, - new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, - new() { Name = ""Custom C"", Id = ""C"" } + new() { Name = ""Custom A"", Id = ""A"", Icon = BitIconName.Emoji }, + new() { Name = ""Custom B"", Id = ""B"", Icon = BitIconName.Emoji }, + new() { Name = ""Custom C"", Id = ""C"", Icon = BitIconName.Emoji2 } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -443,7 +443,7 @@ public class MenuActionItem ItemButton = ""background: lightcoral;"", Callout = ""border-radius: 0.25rem; box-shadow: lightgray 0 0 0.5rem;"" })"" />"; private readonly string example8CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -451,18 +451,18 @@ public class MenuActionItem public bool Disabled { get; set; } } -private static List<MenuActionItem> itemStyleClassCustoms = +private static List<Operation> itemStyleClassCustoms = [ new() { Name = ""Custom A (Default)"", Id = ""A"", Icon = BitIconName.Emoji }, new() { Name = ""Custom B (Styled)"", Id = ""B"", Icon = BitIconName.Emoji, Style = ""color: tomato; border-color: brown; background-color: peachpuff;"" }, new() { Name = ""Custom C (Classed)"", Id = ""C"", Icon = BitIconName.Emoji2, Class = ""custom-item"" }, ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -493,30 +493,30 @@ Custom Header! <BitMenuButton Text=""Customs"" Items=""itemTemplateCustoms2"" NameSelectors=""nameSelectors"" />"; private readonly string example9CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } public string? Icon { get; set; } public bool Disabled { get; set; } - public RenderFragment<MenuActionItem>? Fragment { get; set; } + public RenderFragment<Operation>? Fragment { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private List<MenuActionItem> itemTemplateCustoms = +private List<Operation> itemTemplateCustoms = [ new() { Name = ""Add"", Id = ""add-key"", Icon = BitIconName.Add }, new() { Name = ""Edit"", Id = ""edit-key"", Icon = BitIconName.Edit }, new() { Name = ""Delete"", Id = ""delete-key"", Icon = BitIconName.Delete } ]; -private List<MenuActionItem> itemTemplateCustoms2 = +private List<Operation> itemTemplateCustoms2 = [ new() { @@ -535,36 +535,36 @@ public class MenuActionItem } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false }, - Template = { Name = nameof(MenuActionItem.Fragment) } + Template = { Name = nameof(Operation.Fragment) } };"; private readonly string example10RazorCode = @" <BitMenuButton Text=""Customs"" Items=""basicCustoms"" NameSelectors=""nameSelectors"" - OnChange=""(MenuActionItem item) => eventsChangedCustom = item?.Id"" - OnClick=""(MenuActionItem item) => eventsClickedCustom = item?.Id"" /> + OnChange=""(Operation item) => eventsChangedCustom = item?.Id"" + OnClick=""(Operation item) => eventsClickedCustom = item?.Id"" /> <BitMenuButton Split Text=""Customs"" Items=""basicCustomsOnClick"" NameSelectors=""nameSelectors"" - OnChange=""(MenuActionItem item) => eventsChangedCustom = item?.Id"" - OnClick=""@((MenuActionItem item) => eventsClickedCustom = ""Main button clicked"")"" /> + OnChange=""(Operation item) => eventsChangedCustom = item?.Id"" + OnClick=""@((Operation item) => eventsClickedCustom = ""Main button clicked"")"" /> <BitMenuButton Sticky Items=""basicCustoms"" NameSelectors=""nameSelectors"" - OnChange=""(MenuActionItem item) => eventsChangedCustom = item?.Id"" - OnClick=""(MenuActionItem item) => eventsClickedCustom = item?.Id"" /> + OnChange=""(Operation item) => eventsChangedCustom = item?.Id"" + OnClick=""(Operation item) => eventsClickedCustom = item?.Id"" /> <BitMenuButton Sticky Split Items=""basicCustomsOnClick"" NameSelectors=""nameSelectors"" - OnChange=""(MenuActionItem item) => eventsChangedCustom = item?.Id"" - OnClick=""(MenuActionItem item) => eventsClickedCustom = item?.Id"" /> + OnChange=""(Operation item) => eventsChangedCustom = item?.Id"" + OnClick=""(Operation item) => eventsClickedCustom = item?.Id"" /> <div>Changed custom item: @eventsChangedCustom</div> <div>Clicked custom item: @eventsClickedCustom</div>"; private readonly string example10CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -572,25 +572,25 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private List<MenuActionItem> basicCustomsOnClick = +private List<Operation> basicCustomsOnClick = [ new() { Name = ""Custom A"", Id = ""A"", Icon = BitIconName.Emoji }, new() { Name = ""Custom B"", Id = ""B"", Icon = BitIconName.Emoji }, new() { Name = ""Custom C"", Id = ""C"", Icon = BitIconName.Emoji2 } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -608,11 +608,11 @@ public class MenuActionItem <BitMenuButton Sticky Items=""basicCustoms"" NameSelectors=""nameSelectors"" @bind-IsOpen=""twoWayIsOpen"" /> <BitCheckbox Label=""Two-way IsOpen"" @bind-Value=""twoWayIsOpen"" />"; private readonly string example11CsharpCode = @" -private MenuActionItem twoWaySelectedCustom = default!; +private Operation twoWaySelectedCustom = default!; private bool oneWayIsOpen; private bool twoWayIsOpen; -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -620,25 +620,25 @@ public class MenuActionItem public bool Disabled { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private static List<MenuActionItem> isSelectedCustoms = +private static List<Operation> isSelectedCustoms = [ new() { Name = ""Custom A"", Id = ""A"", Icon = BitIconName.Emoji }, new() { Name = ""Custom B"", Id = ""B"", Icon = BitIconName.Emoji }, new() { Name = ""Custom C"", Id = ""C"", Icon = BitIconName.Emoji2, IsSelected = true } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; @@ -646,7 +646,7 @@ public class MenuActionItem <BitMenuButton Text=""گزینه ها"" Dir=""BitDir.Rtl"" Items=""rtlCustoms"" IconName=""@BitIconName.Edit"" NameSelectors=""nameSelectors"" /> <BitMenuButton Text=""گرینه ها"" Dir=""BitDir.Rtl"" Items=""rtlCustoms"" ChevronDownIcon=""@BitIconName.DoubleChevronDown"" NameSelectors=""nameSelectors"" Split />"; private readonly string example12CsharpCode = @" -public class MenuActionItem +public class Operation { public string? Id { get; set; } public string? Name { get; set; } @@ -656,25 +656,25 @@ public class MenuActionItem public string? Style { get; set; } } -private List<MenuActionItem> basicCustoms = +private List<Operation> basicCustoms = [ new() { Name = ""Custom A"", Id = ""A"" }, new() { Name = ""Custom B"", Id = ""B"", Disabled = true }, new() { Name = ""Custom C"", Id = ""C"" } ]; -private static List<MenuActionItem> itemStyleClassCustoms = +private static List<Operation> itemStyleClassCustoms = [ new() { Name = ""Custom A"", Id = ""A"", Icon = BitIconName.Emoji, Style = ""color:red"" }, new() { Name = ""Custom B"", Id = ""B"", Icon = BitIconName.Emoji, Class = ""custom-item"" }, new() { Name = ""Custom C"", Id = ""C"", Icon = BitIconName.Emoji2, Style = ""background:blue"" } ]; -private BitMenuButtonNameSelectors<MenuActionItem> nameSelectors = new() +private BitMenuButtonNameSelectors<Operation> nameSelectors = new() { - Text = { Name = nameof(MenuActionItem.Name) }, - Key = { Name = nameof(MenuActionItem.Id) }, - IconName = { Name = nameof(MenuActionItem.Icon) }, + Text = { Name = nameof(Operation.Name) }, + Key = { Name = nameof(Operation.Id) }, + IconName = { Name = nameof(Operation.Icon) }, IsEnabled = { Selector = m => m.Disabled is false } };"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/DataGrid/BitDataGridDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/DataGrid/BitDataGridDemo.razor.cs index 14be11e3b9..23091f2b64 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/DataGrid/BitDataGridDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/DataGrid/BitDataGridDemo.razor.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Services; -using Bit.BlazorUI.Demo.Shared.Dtos.DataGridDemo; +using Bit.BlazorUI.Demo.Shared.Dtos.DataGridDemo; namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Extras.DataGrid; @@ -102,6 +101,7 @@ This is normally used in conjunction with a Paginator component or some other UI { Id = "BitDataGridColumnBase", Title = "BitDataGridColumnBase", + Description = "BitDataGrid has two built-in column types, BitDataGridPropertyColumn and BitDataGridTemplateColumn. You can also create your own column types by subclassing ColumnBase he BitDataGridColumnBase type, which all column must derive from, offers some common parameters", Parameters= [ new() @@ -167,14 +167,13 @@ UI will be included in the header cell by default. Description = "If specified, virtualized grids will use this template to render cells whose data has not yet been loaded.", } ], - Description = @"BitDataGrid has two built-in column types, BitDataGridPropertyColumn and BitDataGridTemplateColumn. You can also create your own column types by subclassing ColumnBase - The BitDataGridColumnBase type, which all column must derive from, offers some common parameters", }, new() { Id="BitDataGridPropertyColumn", Title = "BitDataGridPropertyColumn", + Description = "It is for displaying a single value specified by the parameter Property. This column infers sorting rules automatically, and uses the property's name as its title if not otherwise set.", Parameters= [ new() @@ -191,12 +190,12 @@ UI will be included in the header cell by default. Description = "Optionally specifies a format string for the value. Using this requires the TProp type to implement IFormattable.", }, ], - Description = "It is for displaying a single value specified by the parameter Property. This column infers sorting rules automatically, and uses the property's name as its title if not otherwise set.", }, new() { Id = "BitDataGridTemplateColumn", Title = "BitDataGridTemplateColumn", + Description = @"It uses arbitrary Razor fragments to supply contents for its cells. It can't infer the column's title or sort order automatically. also it's possible to add arbitrary Blazor components to your table cells. Remember that rendering many components, or many event handlers, can impact the performance of your grid. One way to mitigate this issue is by paginating or virtualizing your grid", Parameters = [ new() @@ -213,13 +212,8 @@ UI will be included in the header cell by default. Description = "Optionally specifies sorting rules for this column.", }, ], - Description = @"It uses arbitrary Razor fragments to supply contents for its cells. - It can't infer the column's title or sort order automatically. - also it's possible to add arbitrary Blazor components to your table cells. - Remember that rendering many components, or many event handlers, can impact the performance of your grid. One way to mitigate this issue is by paginating or virtualizing your grid", }, - ]; private readonly List<ComponentSubEnum> componentSubEnums = diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor new file mode 100644 index 0000000000..cf249bdaef --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor @@ -0,0 +1,41 @@ +@page "/components/pdfreader" + +@inherits AppComponentBase + +<PageOutlet Url="components/pdfreader" + Title="PdfReader" + Description="pdfreader component of the bit BlazorUI components" /> + +<ComponentDemo ComponentName="PdfReader" + ComponentDescription="BitPdfReader is a simple pdf renderer utilizing the pdfjs library to bring pdf reading feature into Blazor world." + Notes="To use this component, you need to install the `Bit.BlazorUI.Extras` nuget package, as described in the Optional steps of the Getting started page." + ComponentParameters="componentParameters" + ComponentSubClasses="componentSubClasses"> + <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> + <ExamplePreview> + <BitPdfReader Config="basicConfig" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="RenderAllPages" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> + <ExamplePreview> + <BitPdfReader RenderAllPages Horizontal Config="renderAllConfig" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Public API" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> + <ExamplePreview> + <div> + <BitButton OnClick="() => publicApiPdfReaderRef!.First()">First</BitButton> + <BitButton OnClick="() => publicApiPdfReaderRef!.Prev()">Prev</BitButton> + <BitTag Variant="BitVariant.Outline" Text="@($"{publicApiPdfReaderRef?.CurrentPageNumber.ToString()}/{publicApiPdfReaderRef?.NumberOfPages.ToString()}")" Color="BitColor.Info" /> + <BitButton OnClick="() => publicApiPdfReaderRef!.Next()">Next</BitButton> + <BitButton OnClick="() => publicApiPdfReaderRef!.Last()">Last</BitButton> | + <BitButton OnClick="ZoomOut">Zoom -</BitButton> + <BitButton OnClick="ZoomIn">Zoom +</BitButton> + </div> + <br /> + <BitPdfReader @ref="publicApiPdfReaderRef" Config="publicApiConfig" OnPdfLoaded="() => StateHasChanged()" /> + </ExamplePreview> + </ComponentExampleBox> +</ComponentDemo> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor.cs new file mode 100644 index 0000000000..25109b1894 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor.cs @@ -0,0 +1,191 @@ + +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Extras.PdfReader; + +public partial class BitPdfReaderDemo +{ + private readonly List<ComponentParameter> componentParameters = + [ + new() + { + Name = "CanvasClass", + Type = "string?", + DefaultValue = "null", + Description = "The CSS class of the canvas element(s).", + }, + new() + { + Name = "CanvasStyle", + Type = "string?", + DefaultValue = "null", + Description = "The CSS style of the canvas element(s).", + }, + new() + { + Name = "Class", + Type = "string?", + DefaultValue = "null", + Description = "The CSS class of the root element.", + }, + new() + { + Name = "Config", + Type = "BitPdfReaderConfig", + DefaultValue = "", + Description = "The configuration of the pdf reader.", + LinkType = LinkType.Link, + Href = "#pdf-reader-config" + }, + new() + { + Name = "Horizontal", + Type = "bool", + DefaultValue = "false", + Description = "Renders the pages horizontally.", + }, + new() + { + Name = "InitialPageNumber", + Type = "int", + DefaultValue = "1", + Description = "The page number to render initially.", + }, + new() + { + Name = "OnPdfPageRendered", + Type = "EventCallback", + DefaultValue = "", + Description = "The callback for when the pdf page is done rendering.", + }, + new() + { + Name = "OnPdfLoaded", + Type = "EventCallback", + DefaultValue = "", + Description = "The callback for when the pdf document is done loading and processing.", + }, + new() + { + Name = "RenderAllPages", + Type = "bool", + DefaultValue = "false", + Description = "Whether render all pages at start.", + }, + new() + { + Name = "Style", + Type = "string?", + DefaultValue = "null", + Description = "The CSS style of the root element.", + }, + ]; + + private readonly List<ComponentSubClass> componentSubClasses = + [ + new() + { + Id = "pdf-reader-config", + Title = "BitPdfReaderConfig", + Parameters= + [ + new() + { + Name = "Id", + Type = "string", + DefaultValue = "Guid.NewGuid().ToString()", + Description = "The id of the pdf reader instance and its canvas element(s).", + }, + new() + { + Name = "Url", + Type = "string", + DefaultValue = "", + Description = "The URL of the pdf file.", + }, + new() + { + Name = "Scale", + Type = "decimal", + DefaultValue = "1", + Description = "The scale in which the pdf document gets rendered on the page.", + }, + new() + { + Name = "AutoScale", + Type = "bool", + DefaultValue = "true", + Description = "Automatically scales the pdf based on the device pixel-ratio.", + }, + ] + } + ]; + + private readonly BitPdfReaderConfig basicConfig = new() { Url = "/_content/Bit.BlazorUI.Demo.Client.Core/samples/hello-world.pdf" }; + + private readonly BitPdfReaderConfig renderAllConfig = new() { Url = "/_content/Bit.BlazorUI.Demo.Client.Core/samples/article.pdf" }; + + + private double scale = 1; + private BitPdfReader publicApiPdfReaderRef = default!; + private BitPdfReaderConfig publicApiConfig = new() { Url = "/_content/Bit.BlazorUI.Demo.Client.Core/samples/article.pdf" }; + + private async Task ZoomIn() + { + scale += 0.25; + publicApiConfig.Scale = scale; + await publicApiPdfReaderRef.Refresh(); + } + + private async Task ZoomOut() + { + if (scale > 0.25) + { + scale -= 0.25; + } + publicApiConfig.Scale = scale; + await publicApiPdfReaderRef.Refresh(); + } + + + + private readonly string example1RazorCode = @" +<BitPdfReader Config=""basicConfig"" />"; + private readonly string example1CsharpCode = @" +private readonly BitPdfReaderConfig basicConfig = new() { Url = ""url-to-the-pdf-file.pdf"" };"; + + private readonly string example2RazorCode = @" +<BitPdfReader RenderAllPages Horizontal Config=""renderAllConfig"" />"; + private readonly string example2CsharpCode = @" +private readonly BitPdfReaderConfig renderAllConfig = new() { Url = ""url-to-the-pdf-file.pdf"" };"; + + private readonly string example3RazorCode = @" +<BitButton OnClick=""() => publicApiPdfReaderRef!.First()"">First</BitButton> +<BitButton OnClick=""() => publicApiPdfReaderRef!.Prev()"">Prev</BitButton> +<BitTag Variant=""BitVariant.Outline"" Text=""@($""{publicApiPdfReaderRef?.CurrentPageNumber.ToString()}/{publicApiPdfReaderRef?.NumberOfPages.ToString()}"")"" Color=""BitColor.Info"" /> +<BitButton OnClick=""() => publicApiPdfReaderRef!.Next()"">Next</BitButton> +<BitButton OnClick=""() => publicApiPdfReaderRef!.Last()"">Last</BitButton> +<BitButton OnClick=""ZoomOut"">Zoom -</BitButton> +<BitButton OnClick=""ZoomIn"">Zoom +</BitButton> + +<BitPdfReader @ref=""publicApiPdfReaderRef"" Config=""publicApiConfig"" />"; + private readonly string example3CsharpCode = @" +private double scale = 1; +private BitPdfReader publicApiPdfReaderRef = default!; +private BitPdfReaderConfig publicApiConfig = new() { Url = ""url-to-the-pdf-file.pdf"" }; + +private async Task ZoomIn() +{ + scale += 0.25; + publicApiConfig.Scale = scale; + await publicApiPdfReaderRef.Refresh(); +} + +private async Task ZoomOut() +{ + if (scale > 0.25) + { + scale -= 0.25; + } + publicApiConfig.Scale = scale; + await publicApiPdfReaderRef.Refresh(); +}"; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor.scss similarity index 100% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor.scss rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/PdfReader/BitPdfReaderDemo.razor.scss diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.cs index bcd67efce3..fb5395e254 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.cs @@ -35,6 +35,13 @@ public partial class BitChoiceGroupDemo Description = "Default selected Value for ChoiceGroup." }, new() + { + Name = "Inline", + Type = "bool", + DefaultValue = "false", + Description = "Renders the icons and images in a single line with the items in the ChoiceGroup." + }, + new() { Name = "Horizontal", Type = "bool", @@ -98,6 +105,13 @@ public partial class BitChoiceGroupDemo LinkType = LinkType.Link, }, new() + { + Name = "NoCircle", + Type = "bool", + DefaultValue = "false", + Description = "Removes the circle from the start of each item." + }, + new() { Name = "OnClick", Type = "EventCallback<MouseEventArgs>", @@ -233,6 +247,13 @@ public partial class BitChoiceGroupDemo Type = "int", DefaultValue = "null", Description = "Index of the BitChoiceGroup item. This property's value is set by the component at render.", + }, + new() + { + Name = "IsSelected", + Type = "bool", + DefaultValue = "false", + Description = "Determines if the item is selected. This property's value is assigned by the component.", } ] }, @@ -346,6 +367,13 @@ public partial class BitChoiceGroupDemo Type = "int", DefaultValue = "null", Description = "Index of the BitChoiceGroup option. This property's value is set by the component at render.", + }, + new() + { + Name = "IsSelected", + Type = "bool", + DefaultValue = "false", + Description = "Determines if the option is selected. This property's value is assigned by the component.", } ] }, @@ -452,6 +480,13 @@ public partial class BitChoiceGroupDemo Type = "string", DefaultValue = "nameof(BitChoiceGroupItem<TValue>.Index))", Description = "The Index field name of the custom input class. This property's value is set by the component at render.", + }, + new() + { + Name = "IsSelected", + Type = "string", + DefaultValue = "nameof(BitChoiceGroupItem<TValue>.IsSelected))", + Description = "The IsSelected field name of the custom input class. This property's value is assigned by the component.", } ] }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.scss index 1186e590be..dc912dd3c1 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/BitChoiceGroupDemo.razor.scss @@ -7,7 +7,6 @@ flex-wrap: wrap; &.column { - max-width: 19rem; flex-direction: column; } } @@ -35,7 +34,6 @@ width: 8px; height: 8px; border: none; - inset-block-start: 6px; inset-inline-start: 6px; background-color: whitesmoke; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/ChoiceModel.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/Order.cs similarity index 86% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/ChoiceModel.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/Order.cs index 869be442a7..6116408d91 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/ChoiceModel.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/Order.cs @@ -1,6 +1,6 @@ namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.ChoiceGroup; -public class ChoiceModel +public class Order { public string? Name { get; set; } public string? ItemValue { get; set; } @@ -12,6 +12,6 @@ public class ChoiceModel public bool IsDisabled { get; set; } public string? Class { get; set; } public string? Style { get; set; } - public RenderFragment<ChoiceModel>? Fragment { get; set; } + public RenderFragment<Order>? Fragment { get; set; } public int Idx { get; set; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor index 5d59dd4393..f8ec8367fb 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor @@ -1,9 +1,17 @@ <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <BitChoiceGroup Label="Basic Customs" - Items="basicCustoms" - DefaultValue="basicCustoms[1].ItemValue" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + <div class="example-content"> + <BitChoiceGroup Label="Basic Customs" + Items="basicCustoms" + DefaultValue="basicCustoms[1].ItemValue" + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> + + <BitChoiceGroup Label="NoCircle" + NoCircle + Items="basicCustoms" + DefaultValue="basicCustoms[1].ItemValue" + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> + </div> </ExamplePreview> </ComponentExampleBox> @@ -16,7 +24,7 @@ IsEnabled="false" Items="basicCustoms" DefaultValue="@("A")" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <BitChoiceGroup Label="ChoiceGroup with Disabled Custom" Items="disabledCustoms" @@ -31,21 +39,44 @@ <ComponentExampleBox Title="Images and Icons" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> <div>Showcases BitChoiceGroup with image and icon items.</div> - <br /> + <br /><br /> <div class="example-content"> <BitChoiceGroup Label="Image Customs" - DefaultValue="@("Bar")" Items="imageCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - ImageSrc = { Name = nameof(ChoiceModel.ImageAddress) }, - ImageAlt = { Name = nameof(ChoiceModel.ImageDescription) }, - ImageSize = { Name = nameof(ChoiceModel.ImageSize) }, - SelectedImageSrc = { Name = nameof(ChoiceModel.SelectedImageAddress) }})" /> + DefaultValue="@("Bar")" + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})" /> <BitChoiceGroup Label="Icon Customs" + Items="iconCustoms" DefaultValue="@("Day")" + NameSelectors="@(new() { Text = { Selector = i => i.Name }, + Value = { Selector = i => i.ItemValue }, + IconName = { Selector = i => i.IconName }, + IsEnabled = { Selector = i => i.IsDisabled is false } })" /> + </div> + <br /><br /><br /><br /> + <div>Inline:</div><br /> + <div class="example-content"> + <BitChoiceGroup Label="Image Customs" + Inline + Items="inlineImageCustoms" + DefaultValue="@("Bar")" + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})" /> + + <BitChoiceGroup Label="Icon Customs" + Inline Items="iconCustoms" + DefaultValue="@("Day")" NameSelectors="@(new() { Text = { Selector = i => i.Name }, Value = { Selector = i => i.ItemValue }, IconName = { Selector = i => i.IconName }, @@ -63,7 +94,7 @@ Horizontal DefaultValue="@("A")" Items="basicCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <BitChoiceGroup Label="Disabled" Horizontal @@ -76,12 +107,12 @@ Horizontal DefaultValue="@("Bar")" Items="imageCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - ImageSrc = { Name = nameof(ChoiceModel.ImageAddress) }, - ImageAlt = { Name = nameof(ChoiceModel.ImageDescription) }, - ImageSize = { Name = nameof(ChoiceModel.ImageSize) }, - SelectedImageSrc = { Name = nameof(ChoiceModel.SelectedImageAddress) }})" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})" /> <BitChoiceGroup Label="Icon" Horizontal @@ -106,7 +137,7 @@ Items="basicCustoms" DefaultValue="basicCustoms[1].ItemValue" Style="margin-inline: 16px; text-shadow: red 0 0 8px;" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <BitChoiceGroup Label="Classed ChoiceGroup" Items="basicCustoms" @@ -120,10 +151,10 @@ <div class="example-content column"> <BitChoiceGroup Items="itemStyleClassCustoms" DefaultValue="itemStyleClassCustoms[1].ItemValue" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - Class = { Name = nameof(ChoiceModel.Class) }, - Style = { Name = nameof(ChoiceModel.Style) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + Class = { Name = nameof(Order.Class) }, + Style = { Name = nameof(Order.Style) } })" /> </div> <br /><br /><br /><br /> <div><b>Styles</b> & <b>Classes</b>:</div> @@ -136,7 +167,7 @@ ItemLabel = "width: 100%; cursor: pointer;", ItemChecked = "--item-background: #87cefa24; --item-border: 1px solid dodgerblue;", ItemContainer = "padding: 8px; border-radius: 2px; background-color: var(--item-background); border: var(--item-border);" })" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <BitChoiceGroup Label="Classes" Items="basicCustoms" @@ -156,7 +187,7 @@ <br /> <BitChoiceGroup Items="basicCustoms" DefaultValue="@("A")" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"> <LabelTemplate> <div class="custom-label"> Custom label <BitIcon IconName="@BitIconName.Filter" /> @@ -174,7 +205,7 @@ <div> <BitChoiceGroup Label="One-way" Value="@oneWayValue" Items="basicCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <br /> <BitTextField @bind-Value="oneWayValue" /> </div> @@ -197,7 +228,7 @@ <BitChoiceGroup Label="ItemPrefixTemplate" Items="basicCustoms" DefaultValue="@string.Empty" NameSelectors="@(new() { Text = { Selector = i => i.Name}, Value = { Selector = i => i.ItemValue }, - Index = nameof(ChoiceModel.Idx) })"> + Index = nameof(Order.Idx) })"> <ItemPrefixTemplate Context="item"> @(item.Idx + 1). </ItemPrefixTemplate> @@ -218,7 +249,7 @@ <div class="example-content"> <BitChoiceGroup Label="ItemTemplate" @bind-Value="itemTemplateValue" Items="itemTemplateCustoms" - NameSelectors="@(new() { Value = { Name = nameof(ChoiceModel.ItemValue) } })"> + NameSelectors="@(new() { Value = { Name = nameof(Order.ItemValue) } })"> <ItemTemplate Context="custom"> <div class="custom-container @(itemTemplateValue == custom.ItemValue ? "selected" : string.Empty)"> <div class="custom-circle"></div> @@ -228,9 +259,9 @@ </BitChoiceGroup> <BitChoiceGroup Label="Item's Template" Items="itemTemplateCustoms2" @bind-Value="itemTemplateValue2" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - Template = { Name = nameof(ChoiceModel.Fragment) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + Template = { Name = nameof(Order.Fragment) } })" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -244,7 +275,7 @@ Dir="BitDir.Rtl" DefaultValue="@("A")" Items="rtlCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <BitChoiceGroup Label="غیرفعال" Dir="BitDir.Rtl" @@ -267,7 +298,7 @@ <BitChoiceGroup @bind-Value="validationModel.Value" Items="basicCustoms" - NameSelectors="@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })" /> + NameSelectors="@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })" /> <ValidationMessage For="@(() => validationModel.Value)" /> <br /> @@ -285,7 +316,7 @@ </ComponentExampleBox> @code { - private List<ChoiceModel> itemTemplateCustoms2 = default!; + private List<Order> itemTemplateCustoms2 = default!; protected override void OnInitialized() { @@ -296,27 +327,27 @@ Name = "Day", ItemValue = "Day", Fragment = (item => @<div class="custom-container @(itemTemplateValue2 == item.ItemValue ? "selected" : "")"> - <div class="custom-circle" /> - <span style="color:red">@item.Name</span> - </div>) + <div class="custom-circle" /> + <span style="color:red">@item.Name</span> + </div>) }, new() { Name = "Week", ItemValue = "Week", Fragment = (item => @<div class="custom-container @(itemTemplateValue2 == item.ItemValue ? "selected" : "")"> - <div class="custom-circle" /> - <span style="color:green">@item.Name</span> - </div>) + <div class="custom-circle" /> + <span style="color:green">@item.Name</span> + </div>) }, new() { Name = "Month", ItemValue = "Month", Fragment = (item => @<div class="custom-container @(itemTemplateValue2 == item.ItemValue ? "selected" : "")"> - <div class="custom-circle" /> - <span style="color:blue">@item.Name</span> - </div>) + <div class="custom-circle" /> + <span style="color:blue">@item.Name</span> + </div>) } }; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.cs index 18f33a29c7..03b15658b3 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.cs @@ -11,22 +11,24 @@ public partial class _BitChoiceGroupCustomDemo public string? successMessage; - private readonly List<ChoiceModel> basicCustoms = new() - { + private readonly List<Order> basicCustoms = + [ new() { Name = "Custom A", ItemValue = "A" }, new() { Name = "Custom B", ItemValue = "B" }, new() { Name = "Custom C", ItemValue = "C" }, new() { Name = "Custom D", ItemValue = "D" } - }; - private readonly List<ChoiceModel> disabledCustoms = new() - { + ]; + + private readonly List<Order> disabledCustoms = + [ new() { Name = "Custom A", ItemValue = "A" }, new() { Name = "Custom B", ItemValue = "B" }, new() { Name = "Custom C", ItemValue = "C", IsDisabled = true }, new() { Name = "Custom D", ItemValue = "D" } - }; - private readonly List<ChoiceModel> imageCustoms = new() - { + ]; + + private readonly List<Order> imageCustoms = + [ new() { Name = "Bar", @@ -45,33 +47,59 @@ public partial class _BitChoiceGroupCustomDemo ImageAddress= "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png", SelectedImageAddress = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png", } - }; - private readonly List<ChoiceModel> iconCustoms = new() - { + ]; + + private readonly List<Order> inlineImageCustoms = + [ + new() + { + Name = "Bar", + ItemValue = "Bar", + ImageSize = new BitImageSize(20, 20), + ImageDescription = "alt for Bar image", + ImageAddress = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png", + SelectedImageAddress = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png", + }, + new() + { + Name = "Pie", + ItemValue = "Pie", + ImageSize = new BitImageSize(20, 20), + ImageDescription = "alt for Pie image", + ImageAddress= "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png", + SelectedImageAddress = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png", + } + ]; + + private readonly List<Order> iconCustoms = + [ new() { Name = "Day", ItemValue = "Day", IconName = BitIconName.CalendarDay }, new() { Name = "Week", ItemValue = "Week", IconName = BitIconName.CalendarWeek }, new() { Name = "Month", ItemValue = "Month", IconName = BitIconName.Calendar, IsDisabled = true } - }; - private readonly List<ChoiceModel> itemStyleClassCustoms = new() - { + ]; + + private readonly List<Order> itemStyleClassCustoms = + [ new() { Name = "Custom A", ItemValue = "A", Class = "custom-item" }, new() { Name = "Custom B", ItemValue = "B", Style = "padding: 8px; border-radius: 20px; border: 1px solid gray;" }, new() { Name = "Custom C", ItemValue = "C", Class = "custom-item" }, new() { Name = "Custom D", ItemValue = "D", Class = "custom-item" } - }; - private readonly List<ChoiceModel> itemTemplateCustoms = new() - { + ]; + + private readonly List<Order> itemTemplateCustoms = + [ new() { Name = "Day", ItemValue = "Day", IconName = BitIconName.CalendarDay }, new() { Name = "Week", ItemValue = "Week", IconName = BitIconName.CalendarWeek }, new() { Name = "Month", ItemValue = "Month", IconName = BitIconName.Calendar } - }; - private readonly List<ChoiceModel> rtlCustoms = new() - { + ]; + + private readonly List<Order> rtlCustoms = + [ new() { Name = "ویژه آ", ItemValue = "A" }, new() { Name = "ویژه ب", ItemValue = "B" }, new() { Name = "ویژه پ", ItemValue = "C" }, new() { Name = "ویژه ت", ItemValue = "D" } - }; + ]; private void HandleValidSubmit() @@ -89,28 +117,34 @@ private void HandleInvalidSubmit() <BitChoiceGroup Label=""Basic Customs"" Items=""basicCustoms"" DefaultValue=""basicCustoms[1].ItemValue"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" />"; + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> + +<BitChoiceGroup Label=""Basic Customs"" + NoCircle + Items=""basicCustoms"" + DefaultValue=""basicCustoms[1].ItemValue"" + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" />"; private readonly string example1CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -};"; +];"; private readonly string example2RazorCode = @" <BitChoiceGroup Label=""Disabled ChoiceGroup"" IsEnabled=""false"" Items=""basicCustoms"" DefaultValue=""@(""A"")"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitChoiceGroup Label=""ChoiceGroup with Disabled Custom"" Items=""disabledCustoms"" @@ -119,49 +153,70 @@ public class ChoiceModel Value = { Selector = i => i.ItemValue }, IsEnabled = { Selector = i => i.IsDisabled is false } })"" />"; private readonly string example2CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } public bool IsDisabled { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -}; +]; -private readonly List<ChoiceModel> disabledCustoms = new() -{ +private readonly List<Order> disabledCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"", IsDisabled = true }, new() { Name = ""Custom D"", ItemValue = ""D"" } -};"; +];"; private readonly string example3RazorCode = @" <BitChoiceGroup Label=""Image Customs"" - DefaultValue=""@(""Bar"")"" Items=""imageCustoms"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - ImageSrc = { Name = nameof(ChoiceModel.ImageAddress) }, - ImageAlt = { Name = nameof(ChoiceModel.ImageDescription) }, - ImageSize = { Name = nameof(ChoiceModel.ImageSize) }, - SelectedImageSrc = { Name = nameof(ChoiceModel.SelectedImageAddress) }})"" /> + DefaultValue=""@(""Bar"")"" + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})"" /> <BitChoiceGroup Label=""Icon Customs"" + Items=""iconCustoms"" DefaultValue=""@(""Day"")"" + NameSelectors=""@(new() { Text = { Selector = i => i.Name }, + Value = { Selector = i => i.ItemValue }, + IconName = { Selector = i => i.IconName }, + IsEnabled = { Selector = i => i.IsDisabled is false } })"" /> + + +<BitChoiceGroup Label=""Image Customs"" + Inline + Items=""inlineImageCustoms"" + DefaultValue=""@(""Bar"")"" + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})"" /> + +<BitChoiceGroup Label=""Icon Customs"" + Inline Items=""iconCustoms"" + DefaultValue=""@(""Day"")"" NameSelectors=""@(new() { Text = { Selector = i => i.Name }, Value = { Selector = i => i.ItemValue }, IconName = { Selector = i => i.IconName }, IsEnabled = { Selector = i => i.IsDisabled is false } })"" />"; private readonly string example3CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } @@ -173,8 +228,8 @@ public class ChoiceModel public bool IsDisabled { get; set; } } -private readonly List<ChoiceModel> imageCustoms = new() -{ +private readonly List<Order> imageCustoms = +[ new() { Name = ""Bar"", @@ -193,21 +248,43 @@ public class ChoiceModel ImageAddress= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", SelectedImageAddress = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", } -}; +]; -private readonly List<ChoiceModel> iconCustoms = new() -{ +private readonly List<Order> inlineImageCustoms = +[ + new() + { + Name = ""Bar"", + ItemValue = ""Bar"", + ImageSize = new BitImageSize(20, 20), + ImageDescription = ""alt for Bar image"", + ImageAddress = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png"", + SelectedImageAddress = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png"", + }, + new() + { + Name = ""Pie"", + ItemValue = ""Pie"", + ImageSize = new BitImageSize(20, 20), + ImageDescription = ""alt for Pie image"", + ImageAddress= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", + SelectedImageAddress = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", + } +]; + +private readonly List<Order> iconCustoms = +[ new() { Name = ""Day"", ItemValue = ""Day"", IconName = BitIconName.CalendarDay }, new() { Name = ""Week"", ItemValue = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Name = ""Month"", ItemValue = ""Month"", IconName = BitIconName.Calendar, IsDisabled = true } -};"; +];"; private readonly string example4RazorCode = @" <BitChoiceGroup Label=""Basic"" DefaultValue=""@(""A"")"" Items=""basicCustoms"" LayoutFlow=""BitLayoutFlow.Horizontal"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitChoiceGroup Label=""Disabled"" IsEnabled=""false"" @@ -220,12 +297,12 @@ public class ChoiceModel DefaultValue=""@(""Bar"")"" Items=""imageCustoms"" LayoutFlow=""BitLayoutFlow.Horizontal"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - ImageSrc = { Name = nameof(ChoiceModel.ImageAddress) }, - ImageAlt = { Name = nameof(ChoiceModel.ImageDescription) }, - ImageSize = { Name = nameof(ChoiceModel.ImageSize) }, - SelectedImageSrc = { Name = nameof(ChoiceModel.SelectedImageAddress) }})"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + ImageSrc = { Name = nameof(Order.ImageAddress) }, + ImageAlt = { Name = nameof(Order.ImageDescription) }, + ImageSize = { Name = nameof(Order.ImageSize) }, + SelectedImageSrc = { Name = nameof(Order.SelectedImageAddress) }})"" /> <BitChoiceGroup Label=""Icon"" DefaultValue=""@(""Day"")"" @@ -236,7 +313,7 @@ public class ChoiceModel IconName = { Selector = i => i.IconName }, IsEnabled = { Selector = i => i.IsDisabled is false } })"" />"; private readonly string example4CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } @@ -248,16 +325,16 @@ public class ChoiceModel public bool IsDisabled { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -}; +]; -private readonly List<ChoiceModel> imageCustoms = new() -{ +private readonly List<Order> imageCustoms = +[ new() { Name = ""Bar"", @@ -276,14 +353,14 @@ public class ChoiceModel ImageAddress= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", SelectedImageAddress = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", } -}; +]; -private readonly List<ChoiceModel> iconCustoms = new() -{ +private readonly List<Order> iconCustoms = +[ new() { Name = ""Day"", ItemValue = ""Day"", IconName = BitIconName.CalendarDay }, new() { Name = ""Week"", ItemValue = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Name = ""Month"", ItemValue = ""Month"", IconName = BitIconName.Calendar, IsDisabled = true } -};"; +];"; private readonly string example5RazorCode = @" <style> @@ -312,7 +389,6 @@ public class ChoiceModel width: 8px; height: 8px; border: none; - inset-block-start: 6px; inset-inline-start: 6px; background-color: whitesmoke; } @@ -335,7 +411,7 @@ public class ChoiceModel Items=""basicCustoms"" DefaultValue=""basicCustoms[1].ItemValue"" Style=""margin-inline: 16px; text-shadow: red 0 0 8px;"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitChoiceGroup Label=""Classed ChoiceGroup"" Items=""basicCustoms"" @@ -346,10 +422,10 @@ public class ChoiceModel <BitChoiceGroup Items=""itemStyleClassCustoms"" DefaultValue=""itemStyleClassCustoms[1].ItemValue"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - Class = { Name = nameof(ChoiceModel.Class) }, - Style = { Name = nameof(ChoiceModel.Style) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + Class = { Name = nameof(Order.Class) }, + Style = { Name = nameof(Order.Style) } })"" /> <BitChoiceGroup Label=""Styles"" @@ -359,7 +435,7 @@ public class ChoiceModel ItemLabel = ""width: 100%; cursor: pointer;"", ItemChecked = ""--item-background: #87cefa24; --item-border: 1px solid dodgerblue;"", ItemContainer = ""padding: 8px; border-radius: 2px; background-color: var(--item-background); border: var(--item-border);"" })"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitChoiceGroup Label=""Classes"" Items=""basicCustoms"" @@ -370,7 +446,7 @@ public class ChoiceModel ItemLabelWrapper = ""custom-label-wrapper"" })"" NameSelectors=""@(new() { Text = { Selector = i => i.Name }, Value = { Selector = i => i.ItemValue } })""/>"; private readonly string example5CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } @@ -378,21 +454,21 @@ public class ChoiceModel public string? Style { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -}; +]; -private readonly List<ChoiceModel> itemStyleClassCustoms = new() -{ +private readonly List<Order> itemStyleClassCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"", Class = ""custom-item"" }, new() { Name = ""Custom B"", ItemValue = ""B"", Style = ""padding: 8px; border-radius: 20px; border: 1px solid gray;"" }, new() { Name = ""Custom C"", ItemValue = ""C"", Class = ""custom-item"" }, new() { Name = ""Custom D"", ItemValue = ""D"", Class = ""custom-item"" } -};"; +];"; private readonly string example6RazorCode = @" <style> @@ -405,7 +481,7 @@ public class ChoiceModel <BitChoiceGroup Items=""basicCustoms"" DefaultValue=""@(""A"")"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })""> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })""> <LabelTemplate> <div class=""custom-label""> Custom label <BitIcon IconName=""@BitIconName.Filter"" /> @@ -413,24 +489,24 @@ public class ChoiceModel </LabelTemplate> </BitChoiceGroup>"; private readonly string example6CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -};"; +];"; private readonly string example7RazorCode = @" <BitChoiceGroup Label=""One-way"" Value=""@oneWayValue"" Items=""basicCustoms"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitTextField @bind-Value=""oneWayValue"" /> <BitChoiceGroup Label=""Two-way"" @bind-Value=""twoWayValue"" @@ -441,19 +517,19 @@ public class ChoiceModel private string oneWayValue = ""A""; private string twoWayValue = ""A""; -public class ChoiceModel +public class Order { public string? Name { get; set; } public string? ItemValue { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -};"; +];"; private readonly string example8RazorCode = @" <style> @@ -493,7 +569,7 @@ public class ChoiceModel <BitChoiceGroup Label=""ItemPrefixTemplate"" Items=""basicCustoms"" DefaultValue=""@string.Empty"" NameSelectors=""@(new() { Text = { Selector = i => i.Name}, Value = { Selector = i => i.ItemValue }, - Index = nameof(ChoiceModel.Idx) })""> + Index = nameof(Order.Idx) })""> <ItemPrefixTemplate Context=""item""> @(item.Idx + 1). </ItemPrefixTemplate> @@ -512,7 +588,7 @@ public class ChoiceModel <BitChoiceGroup Label=""ItemTemplate"" @bind-Value=""itemTemplateValue"" Items=""itemTemplateCustoms"" - NameSelectors=""@(new() { Value = { Name = nameof(ChoiceModel.ItemValue) } })""> + NameSelectors=""@(new() { Value = { Name = nameof(Order.ItemValue) } })""> <ItemTemplate Context=""custom""> <div class=""custom-container @(itemTemplateValue == custom.ItemValue ? ""selected"" : string.Empty)""> <div class=""custom-circle""></div> @@ -523,43 +599,43 @@ public class ChoiceModel <BitChoiceGroup Label=""Item's Template"" Items=""itemTemplateCustoms2"" @bind-Value=""itemTemplateValue2"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, - Value = { Name = nameof(ChoiceModel.ItemValue) }, - Template = { Name = nameof(ChoiceModel.Fragment) } })"" />"; + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, + Value = { Name = nameof(Order.ItemValue) }, + Template = { Name = nameof(Order.Fragment) } })"" />"; private readonly string example8CsharpCode = @" private string itemLabelTemplateValue = ""Day""; private string itemTemplateValue = ""Day""; private string itemTemplateValue2 = ""Day""; -public class ChoiceModel +public class Order { public string? Name { get; set; } public string? ItemValue { get; set; } public string? IconName { get; set; } - public RenderFragment<ChoiceModel>? Fragment { get; set; } + public RenderFragment<Order>? Fragment { get; set; } public int Idx { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -}; +]; -private readonly List<ChoiceModel> itemTemplateCustoms = new() -{ +private readonly List<Order> itemTemplateCustoms = +[ new() { Name = ""Day"", ItemValue = ""Day"", IconName = BitIconName.CalendarDay }, new() { Name = ""Week"", ItemValue = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Name = ""Month"", ItemValue = ""Month"", IconName = BitIconName.Calendar } -}; +]; -private List<ChoiceModel> itemTemplateCustoms2 = default!; +private List<Order> itemTemplateCustoms2 = default!; protected override void OnInitialized() { - itemTemplateCustoms2 = new() - { + itemTemplateCustoms2 = + [ new() { Name = ""Day"", @@ -587,7 +663,7 @@ protected override void OnInitialized() <span style=""color:blue"">@item.Name</span> </div>) } - }; + ]; }"; private readonly string example9RazorCode = @" @@ -595,7 +671,7 @@ protected override void OnInitialized() Dir=""BitDir.Rtl"" DefaultValue=""@(""A"")"" Items=""rtlCustoms"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <BitChoiceGroup Label=""غیرفعال"" Dir=""BitDir.Rtl"" @@ -604,19 +680,19 @@ protected override void OnInitialized() Items=""rtlCustoms"" NameSelectors=""@(new() { Text = { Selector = i => i.Name }, Value = { Selector = i => i.ItemValue } })"" />"; private readonly string example9CsharpCode = @" -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } } -private readonly List<ChoiceModel> rtlCustoms = new() -{ +private readonly List<Order> rtlCustoms = +[ new() { Name = ""ویژه آ"", ItemValue = ""A"" }, new() { Name = ""ویژه ب"", ItemValue = ""B"" }, new() { Name = ""ویژه پ"", ItemValue = ""C"" }, new() { Name = ""ویژه ت"", ItemValue = ""D"" } -};"; +];"; private readonly string example10RazorCode = @" <style> @@ -630,7 +706,7 @@ public class ChoiceModel <BitChoiceGroup @bind-Value=""validationModel.Value"" Items=""basicCustoms"" - NameSelectors=""@(new() { Text = { Name = nameof(ChoiceModel.Name) }, Value = { Name = nameof(ChoiceModel.ItemValue) } })"" /> + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> <ValidationMessage For=""@(() => validationModel.Value)"" /> <BitButton ButtonType=""BitButtonType.Submit"">Submit</BitButton> @@ -647,17 +723,17 @@ public class ChoiceGroupValidationModel private void HandleValidSubmit() { } private void HandleInvalidSubmit() { } -public class ChoiceModel +public class Order { public string Name { get; set; } public string ItemValue { get; set; } } -private readonly List<ChoiceModel> basicCustoms = new() -{ +private readonly List<Order> basicCustoms = +[ new() { Name = ""Custom A"", ItemValue = ""A"" }, new() { Name = ""Custom B"", ItemValue = ""B"" }, new() { Name = ""Custom C"", ItemValue = ""C"" }, new() { Name = ""Custom D"", ItemValue = ""D"" } -};"; +];"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor index 8e7a7c4fcd..f030f9f2a3 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor @@ -1,6 +1,15 @@ <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <BitChoiceGroup Label="Basic Items" Items="basicItems" DefaultValue="basicItems[1].Value" /> + <div class="example-content"> + <BitChoiceGroup Label="Basic Items" + Items="basicItems" + DefaultValue="basicItems[1].Value" /> + + <BitChoiceGroup Label="NoCircle" + NoCircle + Items="basicItems" + DefaultValue="basicItems[1].Value" /> + </div> </ExamplePreview> </ComponentExampleBox> @@ -24,11 +33,17 @@ <ComponentExampleBox Title="Images and Icons" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> <div>Showcases BitChoiceGroup with image and icon items.</div> - <br /> + <br /><br /> <div class="example-content"> <BitChoiceGroup Label="Image Items" Items="imageItems" DefaultValue="@("Bar")" /> <BitChoiceGroup Label="Icon Items" Items="iconItems" DefaultValue="@("Day")" /> </div> + <br /><br /><br /><br /> + <div>Inline:</div><br /> + <div class="example-content"> + <BitChoiceGroup Label="Image Items" Items="inlineImageItems" DefaultValue="@("Bar")" Inline /> + <BitChoiceGroup Label="Icon Items" Items="iconItems" DefaultValue="@("Day")" Inline /> + </div> </ExamplePreview> </ComponentExampleBox> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.cs index 66f001a822..e7fddce99e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.cs @@ -9,26 +9,28 @@ public partial class _BitChoiceGroupItemDemo private string itemTemplateValue2 = "Day"; private string itemLabelTemplateValue = "Day"; - private ChoiceGroupValidationModel validationModel = new(); private string? successMessage; + private ChoiceGroupValidationModel validationModel = new(); - private readonly List<BitChoiceGroupItem<string>> basicItems = new() - { + private readonly List<BitChoiceGroupItem<string>> basicItems = + [ new() { Text = "Item A", Value = "A" }, - new() { Text = "Item B", Value = "B" }, - new() { Text = "Item C", Value = "C" }, - new() { Text = "Item D", Value = "D" } - }; - private readonly List<BitChoiceGroupItem<string>> disabledItems = new() - { + new() { Text = "Item B", Value = "B" }, + new() { Text = "Item C", Value = "C" }, + new() { Text = "Item D", Value = "D" } + ]; + + private readonly List<BitChoiceGroupItem<string>> disabledItems = + [ new() { Text = "Item A", Value = "A" }, - new() { Text = "Item B", Value = "B" }, - new() { Text = "Item C", Value = "C", IsEnabled = false }, - new() { Text = "Item D", Value = "D" } - }; - private readonly List<BitChoiceGroupItem<string>> imageItems = new() - { + new() { Text = "Item B", Value = "B" }, + new() { Text = "Item C", Value = "C", IsEnabled = false }, + new() { Text = "Item D", Value = "D" } + ]; + + private readonly List<BitChoiceGroupItem<string>> imageItems = + [ new() { Text = "Bar", @@ -47,33 +49,59 @@ public partial class _BitChoiceGroupItemDemo ImageSrc= "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png", SelectedImageSrc = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png", } - }; - private readonly List<BitChoiceGroupItem<string>> iconItems = new() - { + ]; + + private readonly List<BitChoiceGroupItem<string>> inlineImageItems = + [ + new() + { + Text = "Bar", + Value = "Bar", + ImageAlt = "alt for Bar image", + ImageSize = new BitImageSize(20, 20), + ImageSrc= "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png", + SelectedImageSrc = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png", + }, + new() + { + Text = "Pie", + Value = "Pie", + ImageAlt = "alt for Pie image", + ImageSize = new BitImageSize(20, 20), + ImageSrc= "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png", + SelectedImageSrc = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png", + } + ]; + + private readonly List<BitChoiceGroupItem<string>> iconItems = + [ new() { Text = "Day", Value = "Day", IconName = BitIconName.CalendarDay }, new() { Text = "Week", Value = "Week", IconName = BitIconName.CalendarWeek }, new() { Text = "Month", Value = "Month", IconName = BitIconName.Calendar, IsEnabled = false } - }; - private readonly List<BitChoiceGroupItem<string>> itemStyleClassItems = new() - { + ]; + + private readonly List<BitChoiceGroupItem<string>> itemStyleClassItems = + [ new() { Text = "Item A", Value = "A", Class = "custom-item" }, new() { Text = "Item B", Value = "B", Style = "padding: 8px; border-radius: 20px; border: 1px solid gray;" }, new() { Text = "Item C", Value = "C", Class = "custom-item" }, new() { Text = "Item D", Value = "D", Class = "custom-item" } - }; - private readonly List<BitChoiceGroupItem<string>> itemTemplateItems = new() - { + ]; + + private readonly List<BitChoiceGroupItem<string>> itemTemplateItems = + [ new() { Text = "Day", Value = "Day", IconName = BitIconName.CalendarDay }, new() { Text = "Week", Value = "Week", IconName = BitIconName.CalendarWeek }, new() { Text = "Month", Value = "Month", IconName = BitIconName.Calendar } - }; - private readonly List<BitChoiceGroupItem<string>> rtlItems = new() - { + ]; + + private readonly List<BitChoiceGroupItem<string>> rtlItems = + [ new() { Text = "بخش آ", Value = "A" }, new() { Text = "بخش ب", Value = "B" }, new() { Text = "بخش پ", Value = "C" }, new() { Text = "بخش ت", Value = "D" } - }; + ]; private void HandleValidSubmit() @@ -88,15 +116,22 @@ private void HandleInvalidSubmit() private readonly string example1RazorCode = @" -<BitChoiceGroup Label=""Basic Items"" Items=""basicItems"" DefaultValue=""basicItems[1].Value"" />"; +<BitChoiceGroup Label=""Basic Items"" + Items=""basicItems"" + DefaultValue=""basicItems[1].Value"" /> + +<BitChoiceGroup Label=""NoCircle"" + NoCircle + Items=""basicItems"" + DefaultValue=""basicItems[1].Value"" />"; private readonly string example1CsharpCode = @" -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -};"; +];"; private readonly string example2RazorCode = @" <BitChoiceGroup Label=""Disabled ChoiceGroup"" @@ -108,29 +143,32 @@ private void HandleInvalidSubmit() Items=""disabledItems"" DefaultValue=""@(""A"")"" />"; private readonly string example2CsharpCode = @" -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -}; +]; -private readonly List<BitChoiceGroupItem> disabledItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> disabledItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"", IsEnabled = false }, new() { Text = ""Item D"", Value = ""D"" } -};"; +];"; private readonly string example3RazorCode = @" <BitChoiceGroup Label=""Image Items"" Items=""imageItems"" DefaultValue=""@(""Bar"")"" /> -<BitChoiceGroup Label=""Icon Items"" Items=""iconItems"" DefaultValue=""@(""Day"")"" />"; +<BitChoiceGroup Label=""Icon Items"" Items=""iconItems"" DefaultValue=""@(""Day"")"" /> + +<BitChoiceGroup Label=""Image Items"" Items=""inlineImageItems"" DefaultValue=""@(""Bar"")"" Inline /> +<BitChoiceGroup Label=""Icon Items"" Items=""iconItems"" DefaultValue=""@(""Day"")"" Inline />"; private readonly string example3CsharpCode = @" -private readonly List<BitChoiceGroupItem> imageItems<string> = new() -{ - new BitChoiceGroupItem() +private readonly List<BitChoiceGroupItem<string>> imageItems = +[ + new() { Text = ""Bar"", Value = ""Bar"", @@ -139,7 +177,7 @@ private void HandleInvalidSubmit() ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png"", SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png"", }, - new BitChoiceGroupItem() + new() { Text = ""Pie"", Value = ""Pie"", @@ -148,14 +186,36 @@ private void HandleInvalidSubmit() ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", } -}; +]; -private readonly List<BitChoiceGroupItem> iconItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> inlineImageItems = +[ + new() + { + Text = ""Bar"", + Value = ""Bar"", + ImageAlt = ""alt for Bar image"", + ImageSize = new BitImageSize(20, 20), + ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png"", + SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png"", + }, + new() + { + Text = ""Pie"", + Value = ""Pie"", + ImageAlt = ""alt for Pie image"", + ImageSize = new BitImageSize(20, 20), + ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", + SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", + } +]; + +private readonly List<BitChoiceGroupItem<string>> iconItems = +[ new() { Text = ""Day"", Value = ""Day"", IconName = BitIconName.CalendarDay }, new() { Text = ""Week"", Value = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Text = ""Month"", Value = ""Month"", IconName = BitIconName.Calendar, IsEnabled = false } -};"; +];"; private readonly string example4RazorCode = @" <BitChoiceGroup Label=""Basic"" Items=""basicItems"" DefaultValue=""@(""A"")"" LayoutFlow=""BitLayoutFlow.Horizontal"" /> @@ -163,17 +223,17 @@ private void HandleInvalidSubmit() <BitChoiceGroup Label=""Image"" Items=""imageItems"" DefaultValue=""@(""Bar"")"" LayoutFlow=""BitLayoutFlow.Horizontal"" /> <BitChoiceGroup Label=""Icon"" Items=""iconItems"" DefaultValue=""@(""Day"")"" LayoutFlow=""BitLayoutFlow.Horizontal"" />"; private readonly string example4CsharpCode = @" -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -}; +]; -private readonly List<BitChoiceGroupItem> imageItems<string> = new() -{ - new BitChoiceGroupItem() +private readonly List<BitChoiceGroupItem<string>> imageItems = +[ + new() { Text = ""Bar"", Value = ""Bar"", @@ -182,7 +242,7 @@ private void HandleInvalidSubmit() ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png"", SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png"", }, - new BitChoiceGroupItem() + new() { Text = ""Pie"", Value = ""Pie"", @@ -191,14 +251,14 @@ private void HandleInvalidSubmit() ImageSrc= ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"", SelectedImageSrc = ""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"", } -}; +]; -private readonly List<BitChoiceGroupItem> iconItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> iconItems = +[ new() { Text = ""Day"", Value = ""Day"", IconName = BitIconName.CalendarDay }, new() { Text = ""Week"", Value = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Text = ""Month"", Value = ""Month"", IconName = BitIconName.Calendar, IsEnabled = false } -};"; +];"; private readonly string example5RazorCode = @" <style> @@ -227,7 +287,6 @@ private void HandleInvalidSubmit() width: 8px; height: 8px; border: none; - inset-block-start: 6px; inset-inline-start: 6px; background-color: whitesmoke; } @@ -276,21 +335,21 @@ private void HandleInvalidSubmit() ItemChecked = ""custom-checked"", ItemLabelWrapper = ""custom-label-wrapper"" })"" />"; private readonly string example5CsharpCode = @" -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -}; +]; -private readonly List<BitChoiceGroupItem<string>> itemStyleClassItems = new() -{ +private readonly List<BitChoiceGroupItem<string>> itemStyleClassItems = +[ new() { Text = ""Item A"", Value = ""A"", Class = ""custom-item"" }, new() { Text = ""Item B"", Value = ""B"", Style = ""padding: 8px; border-radius: 20px; border: 1px solid gray;"" }, new() { Text = ""Item C"", Value = ""C"", Class = ""custom-item"" }, new() { Text = ""Item D"", Value = ""D"", Class = ""custom-item"" } -};"; +];"; private readonly string example6RazorCode = @" <style> @@ -309,13 +368,13 @@ private void HandleInvalidSubmit() </LabelTemplate> </BitChoiceGroup>"; private readonly string example6CsharpCode = @" -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -};"; +];"; private readonly string example7RazorCode = @" <BitChoiceGroup Label=""One-way"" Items=""basicItems"" Value=""@oneWayValue"" /> @@ -327,13 +386,13 @@ private void HandleInvalidSubmit() private string oneWayValue = ""A""; private string twoWayValue = ""A""; -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -};"; +];"; private readonly string example8RazorCode = @" <style> @@ -401,20 +460,20 @@ private void HandleInvalidSubmit() private string itemTemplateValue2 = ""Day""; private string itemLabelTemplateValue = ""Day""; -private readonly List<BitChoiceGroupItem<string>> basicItems = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -}; +]; -private readonly List<BitChoiceGroupItem> itemTemplateItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> itemTemplateItems = +[ new() { Text = ""Day"", Value = ""Day"", IconName = BitIconName.CalendarDay }, new() { Text = ""Week"", Value = ""Week"", IconName = BitIconName.CalendarWeek }, new() { Text = ""Month"", Value = ""Month"", IconName = BitIconName.Calendar } -}; +]; private List<BitChoiceGroupItem<string>> itemTemplateItems2 = default!; protected override void OnInitialized() @@ -490,11 +549,11 @@ public class ChoiceGroupValidationModel private void HandleValidSubmit() { } private void HandleInvalidSubmit() { } -private readonly List<BitChoiceGroupItem> basicItems<string> = new() -{ +private readonly List<BitChoiceGroupItem<string>> basicItems = +[ new() { Text = ""Item A"", Value = ""A"" }, new() { Text = ""Item B"", Value = ""B"" }, new() { Text = ""Item C"", Value = ""C"" }, new() { Text = ""Item D"", Value = ""D"" } -};"; +];"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor index d63d8f3938..2f74a83394 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor @@ -1,11 +1,20 @@ <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <BitChoiceGroup Label="Basic Options" DefaultValue="@("B")" TItem="BitChoiceGroupOption<string>" TValue="string"> - <BitChoiceGroupOption Text="Option A" Value="@("A")" /> - <BitChoiceGroupOption Text="Option B" Value="@("B")" /> - <BitChoiceGroupOption Text="Option C" Value="@("C")" /> - <BitChoiceGroupOption Text="Option D" Value="@("D")" /> - </BitChoiceGroup> + <div class="example-content"> + <BitChoiceGroup Label="Basic Options" DefaultValue="@("B")" TItem="BitChoiceGroupOption<string>" TValue="string"> + <BitChoiceGroupOption Text="Option A" Value="@("A")" /> + <BitChoiceGroupOption Text="Option B" Value="@("B")" /> + <BitChoiceGroupOption Text="Option C" Value="@("C")" /> + <BitChoiceGroupOption Text="Option D" Value="@("D")" /> + </BitChoiceGroup> + + <BitChoiceGroup Label="NoCircle" NoCircle DefaultValue="@("B")" TItem="BitChoiceGroupOption<string>" TValue="string"> + <BitChoiceGroupOption Text="Option A" Value="@("A")" /> + <BitChoiceGroupOption Text="Option B" Value="@("B")" /> + <BitChoiceGroupOption Text="Option C" Value="@("C")" /> + <BitChoiceGroupOption Text="Option D" Value="@("D")" /> + </BitChoiceGroup> + </div> </ExamplePreview> </ComponentExampleBox> @@ -39,7 +48,7 @@ <ComponentExampleBox Title="Images and Icons" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> <div>Showcases BitChoiceGroup with image and icon items.</div> - <br /> + <br /><br /> <div class="example-content"> <BitChoiceGroup Label="Image Options" DefaultValue="@("Bar")" @@ -66,6 +75,36 @@ <BitChoiceGroupOption Text="Month" Value="@("Month")" IconName="@BitIconName.Calendar" IsEnabled="false" /> </BitChoiceGroup> </div> + <br /><br /><br /><br /> + <div>Inline:</div><br /> + <div class="example-content"> + <BitChoiceGroup Label="Image Options" + Inline + DefaultValue="@("Bar")" + TItem="BitChoiceGroupOption<string>" TValue="string"> + <BitChoiceGroupOption Text="Bar" + Value="@("Bar")" + ImageAlt="Alt for Bar image" + ImageSize="@(new BitImageSize(20, 20))" + ImageSrc="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png" + SelectedImageSrc="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png" /> + <BitChoiceGroupOption Text="Pie" + Value="@("Pie")" + ImageAlt="Alt for Pie image" + ImageSize="@(new BitImageSize(20, 20))" + ImageSrc="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png" + SelectedImageSrc="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png" /> + </BitChoiceGroup> + + <BitChoiceGroup Label="Icon Options" + Inline + DefaultValue="@("Day")" + TItem="BitChoiceGroupOption<string>" TValue="string"> + <BitChoiceGroupOption Text="Day" Value="@("Day")" IconName="@BitIconName.CalendarDay" /> + <BitChoiceGroupOption Text="Week" Value="@("Week")" IconName="@BitIconName.CalendarWeek" /> + <BitChoiceGroupOption Text="Month" Value="@("Month")" IconName="@BitIconName.Calendar" IsEnabled="false" /> + </BitChoiceGroup> + </div> </ExamplePreview> </ComponentExampleBox> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.cs index fbfd943727..183a988d89 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.cs @@ -28,6 +28,13 @@ private void HandleInvalidSubmit() <BitChoiceGroupOption Text=""Option B"" Value=""@(""B"")"" /> <BitChoiceGroupOption Text=""Option C"" Value=""@(""C"")"" /> <BitChoiceGroupOption Text=""Option D"" Value=""@(""D"")"" /> +</BitChoiceGroup> + +<BitChoiceGroup Label=""NoCircle"" NoCircle DefaultValue=""@(""B"")"" TItem=""BitChoiceGroupOption<string>"" TValue=""string""> + <BitChoiceGroupOption Text=""Option A"" Value=""@(""A"")"" /> + <BitChoiceGroupOption Text=""Option B"" Value=""@(""B"")"" /> + <BitChoiceGroupOption Text=""Option C"" Value=""@(""C"")"" /> + <BitChoiceGroupOption Text=""Option D"" Value=""@(""D"")"" /> </BitChoiceGroup>"; private readonly string example2RazorCode = @" @@ -74,6 +81,34 @@ private void HandleInvalidSubmit() <BitChoiceGroupOption Text=""Day"" Value=""@(""Day"")"" IconName=""@BitIconName.CalendarDay"" /> <BitChoiceGroupOption Text=""Week"" Value=""@(""Week"")"" IconName=""@BitIconName.CalendarWeek"" /> <BitChoiceGroupOption Text=""Month"" Value=""@(""Month"")"" IconName=""@BitIconName.Calendar"" IsEnabled=""false"" /> +</BitChoiceGroup> + + +<BitChoiceGroup Label=""Image Options"" + Inline + DefaultValue=""@(""Bar"")"" + TItem=""BitChoiceGroupOption<string>"" TValue=""string""> + <BitChoiceGroupOption Text=""Bar"" + Value=""@(""Bar"")"" + ImageAlt=""Alt for Bar image"" + ImageSize=""@(new BitImageSize(20, 20))"" + ImageSrc=""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-unselected.png"" + SelectedImageSrc=""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-bar-selected.png"" /> + <BitChoiceGroupOption Text=""Pie"" + Value=""@(""Pie"")"" + ImageAlt=""Alt for Pie image"" + ImageSize=""@(new BitImageSize(20, 20))"" + ImageSrc=""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-unselected.png"" + SelectedImageSrc=""https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/choicegroup-pie-selected.png"" /> +</BitChoiceGroup> + +<BitChoiceGroup Label=""Icon Options"" + Inline + DefaultValue=""@(""Day"")"" + TItem=""BitChoiceGroupOption<string>"" TValue=""string""> + <BitChoiceGroupOption Text=""Day"" Value=""@(""Day"")"" IconName=""@BitIconName.CalendarDay"" /> + <BitChoiceGroupOption Text=""Week"" Value=""@(""Week"")"" IconName=""@BitIconName.CalendarWeek"" /> + <BitChoiceGroupOption Text=""Month"" Value=""@(""Month"")"" IconName=""@BitIconName.Calendar"" IsEnabled=""false"" /> </BitChoiceGroup>"; private readonly string example4RazorCode = @" @@ -139,7 +174,6 @@ private void HandleInvalidSubmit() width: 8px; height: 8px; border: none; - inset-block-start: 6px; inset-inline-start: 6px; background-color: whitesmoke; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownDemo.razor.cs index ab81034c30..96cacf31fa 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownDemo.razor.cs @@ -28,9 +28,9 @@ public partial class BitDropdownDemo new() { Name = "CaretDownIconName", - Type = "string", - DefaultValue = "ChevronDown", - Description = "The icon name of the chevron down element of the dropdown. The default value is ChevronDown.", + Type = "string?", + DefaultValue = "null", + Description = "The icon name of the chevron down element of the dropdown.", }, new() { @@ -74,14 +74,14 @@ public partial class BitDropdownDemo Name = "DefaultValue", Type = "string?", DefaultValue = "null", - Description = "The default key value that will be initially used to set selected item if the Value parameter is not set.", + Description = "The default value that will be initially used to set selected item if the Value parameter is not set.", }, new() { Name = "DefaultValues", - Type = "List<string>", - DefaultValue = "new List<string>()", - Description = "The default key value that will be initially used to set selected items in multi select mode if the Values parameter is not set.", + Type = "IEnumerable<string?>?", + DefaultValue = "null", + Description = "The default values that will be initially used to set selected items in multi select mode if the Values parameter is not set.", }, new() { @@ -131,13 +131,6 @@ public partial class BitDropdownDemo Description = "The custom template for rendering the header items of the dropdown.", }, new() - { - Name = "IsMultiSelect", - Type = "bool", - DefaultValue = "false", - Description = "Enables the multi select mode.", - }, - new() { Name = "IsOpen", Type = "bool", @@ -145,20 +138,6 @@ public partial class BitDropdownDemo Description = "Determines the opening state of the callout. (two-way bound)", }, new() - { - Name = "IsReselectable", - Type = "bool", - DefaultValue = "false", - Description = "Enables calling the select events when the same item is selected in single select mode.", - }, - new() - { - Name = "IsResponsive", - Type = "bool", - DefaultValue = "false", - Description = "Enables the responsive mode of the component for small screens.", - }, - new() { Name = "Items", Type = "ICollection<TItem>?", @@ -203,6 +182,13 @@ public partial class BitDropdownDemo Description = "The custom template for the label of the dropdown.", }, new() + { + Name = "MultiSelect", + Type = "bool", + DefaultValue = "false", + Description = "Enables the multi select mode.", + }, + new() { Name = "MultiSelectDelimiter", Type = "string", @@ -252,7 +238,7 @@ public partial class BitDropdownDemo new() { Name = "OnValuesChange", - Type = "EventCallback<TItem[]>", + Type = "EventCallback<IEnumerable<TValue>>", Description = "The callback that called when selected items change.", }, new() @@ -305,6 +291,20 @@ public partial class BitDropdownDemo Description = "Disables automatic setting of the callout width and preserves its original width.", }, new() + { + Name = "Reselectable", + Type = "bool", + DefaultValue = "false", + Description = "Enables calling the select events when the same item is selected in single select mode.", + }, + new() + { + Name = "Responsive", + Type = "bool", + DefaultValue = "false", + Description = "Enables the responsive mode of the component for small screens.", + }, + new() { Name = "SearchBoxPlaceholder", Type = "string?", @@ -379,9 +379,9 @@ public partial class BitDropdownDemo new() { Name = "Values", - Type = "ICollection<TValue?>", + Type = "IEnumerable<TValue?>?", DefaultValue = "null", - Description = "The key values of the selected items in multi select mode. (two-way bound)", + Description = "The values of the selected items in multi select mode. (two-way bound)", }, new() { @@ -485,6 +485,13 @@ public partial class BitDropdownDemo DefaultValue = "null", Description = "The value of the dropdown item." }, + new() + { + Name = "IsSelected", + Type = "bool", + DefaultValue = "false", + Description = "Determines if the item is selected. This property's value is assigned by the component." + }, ], }, new() @@ -579,6 +586,13 @@ public partial class BitDropdownDemo DefaultValue = "null", Description = "The value of the dropdown option." }, + new() + { + Name = "IsSelected", + Type = "bool", + DefaultValue = "false", + Description = "Determines if the option is selected. This property's value is assigned by the component." + }, ], }, new() @@ -697,6 +711,12 @@ public partial class BitDropdownDemo Name = "ValueSetter", Type = "Action<TItem, TItem>?", Description = "The setter function for updating Value property of custom item in Dynamic ComboBox mode upon new item addition.", + }, + new() + { + Name = "IsSelected", + Type = "string", + Description = "The IsSelected field name of the custom input class. This property's value is assigned by the component.", } ], }, @@ -918,7 +938,6 @@ public partial class BitDropdownDemo ], } ]; - private readonly List<ComponentSubEnum> componentSubEnums = [ new() @@ -948,7 +967,6 @@ public partial class BitDropdownDemo ] }, ]; - private readonly List<ComponentParameter> componentPublicMembers = [ new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/FormValidationDropdownModel.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/FormValidationDropdownModel.cs index 04727e2246..70449b1348 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/FormValidationDropdownModel.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/FormValidationDropdownModel.cs @@ -4,7 +4,7 @@ public class FormValidationDropdownModel { [MaxLength(2, ErrorMessage = "The property {0} have more than {1} elements")] [MinLength(1, ErrorMessage = "The property {0} doesn't have at least {1} elements")] - public ICollection<string> Products { get; set; } = []; + public IEnumerable<string> Products { get; set; } = []; [Required] public string Category { get; set; } = default!; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownCustom.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/Product.cs similarity index 94% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownCustom.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/Product.cs index 431a24bc75..bf5aee6e0a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/BitDropdownCustom.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/Product.cs @@ -1,6 +1,6 @@ namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.Dropdown; -public class BitDropdownCustom +public class Product { public string? Label { get; set; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor index 8410439e3c..6d27c53e01 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor @@ -9,10 +9,10 @@ Placeholder="Select an item" /> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" /> + NameSelectors="nameSelectors" /> <br /><br /> <BitDropdown Label="Required" Required Items="GetBasicCustoms()" @@ -22,15 +22,15 @@ <BitDropdown Label="PreserveCalloutWidth" PreserveCalloutWidth Items="GetBasicCustoms()" - NameSelectors="nameSelectors" - Placeholder="Select an item" /> + Placeholder="Select an item" + NameSelectors="nameSelectors" /> <br /><br /> <BitDropdown Label="Disabled" + IsEnabled="false" Items="GetBasicCustoms()" DefaultValue="@("f-ora")" - NameSelectors="nameSelectors" Placeholder="Select an item" - IsEnabled="false" /> + NameSelectors="nameSelectors" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -74,16 +74,18 @@ <ExamplePreview> <div>Enables fit-content width for the dropdown that sets the width of the component based on its content size.</div> <br /><br /> - <BitDropdown Label="Single select" FitWidth + <BitDropdown Label="Single select" + FitWidth Items="GetBasicCustoms()" - NameSelectors="nameSelectors" - Placeholder="Select an item" /> + Placeholder="Select an item" + NameSelectors="nameSelectors" /> <br /><br /> - <BitDropdown Label="Multi select" FitWidth + <BitDropdown Label="Multi select" + FitWidth + MultiSelect Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" /> + NameSelectors="nameSelectors" /> </ExamplePreview> </ComponentExampleBox> @@ -94,14 +96,14 @@ <div style="max-width:10rem"> <BitDropdown NoBorder Items="GetBasicCustoms()" - NameSelectors="nameSelectors" - Placeholder="Select an item" /> + Placeholder="Select an item" + NameSelectors="nameSelectors" /> <br /><br /> <BitDropdown NoBorder + MultiSelect Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" /> + NameSelectors="nameSelectors" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -112,10 +114,10 @@ <br /> <div class="example-content"> <BitDropdown Label="Responsive Dropdown" + Responsive Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select an item" - IsResponsive=true /> + NameSelectors="nameSelectors" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -146,21 +148,21 @@ <br /> <div class="example-content"> <BitDropdown @bind-Value="clearValue" - Label="Single select dropdown" + ShowClearButton Items="GetBasicCustoms()" NameSelectors="nameSelectors" - Placeholder="Select an option" - ShowClearButton="true" /> + Label="Single select dropdown" + Placeholder="Select an option" /> <br /> <div>Value: @clearValue</div> <br /><br /><br /> <BitDropdown @bind-Values="clearValues" - Label="Multi select dropdown" + MultiSelect + ShowClearButton Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select options" - IsMultiSelect="true" - ShowClearButton="true" /> + Label="Multi select dropdown" + NameSelectors="nameSelectors" /> <br /> <div>Values: @string.Join(',', clearValues)</div> </div> @@ -173,39 +175,39 @@ <br /><br /> <div class="example-content"> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" + NameSelectors="nameSelectors" SearchBoxPlaceholder="Search item" /> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" + NameSelectors="nameSelectors" SearchBoxPlaceholder="Search items" /> <br /><br /><br /><br /> <div><b>Custom search function</b>:</div><br /> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" - SearchBoxPlaceholder="Search item" /> + NameSelectors="nameSelectors" + SearchBoxPlaceholder="Search item" + SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" /> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" - SearchBoxPlaceholder="Search items" /> + NameSelectors="nameSelectors" + SearchBoxPlaceholder="Search items" + SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -231,11 +233,11 @@ <br /> <BitDropdown @bind-Values="validationModel.Products" - Label="Select min 1 and max 2 items" + MultiSelect Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" /> + NameSelectors="nameSelectors" + Label="Select min 1 and max 2 items" /> <ValidationMessage For="@(() => validationModel.Products)" /> @@ -347,11 +349,11 @@ <div>Selected Value: @controlledValue</div> <br /><br /> <BitDropdown @bind-Values="controlledValues" + MultiSelect Label="Multi select" Items="GetBasicCustoms()" - NameSelectors="nameSelectors" Placeholder="Select items" - IsMultiSelect="true" /> + NameSelectors="nameSelectors" /> <br /> <div>Selected Values: @string.Join(",", controlledValues)</div> <br /><br /><br /><br /> @@ -360,20 +362,20 @@ Items="GetBasicCustoms()" Placeholder="Select an item" NameSelectors="nameSelectors" - TItem="BitDropdownCustom" TValue="string" - OnValuesChange="(BitDropdownCustom[] items) => changedItem = items.SingleOrDefault()" /> + TItem="Product" TValue="string" + OnChange="(string? value) => changedValue = value" /> <br /> - <div>Changed Value: @changedItem?.Value</div> + <div>Changed Value: @changedValue</div> <br /><br /> <BitDropdown Label="Multi select" - IsMultiSelect="true" + MultiSelect Items="GetBasicCustoms()" Placeholder="Select items" NameSelectors="nameSelectors" - TItem="BitDropdownCustom" TValue="string" - OnValuesChange="(BitDropdownCustom[] items) => changedItems = items" /> + TItem="Product" TValue="string" + OnValuesChange="(IEnumerable<string> values) => changedValues = values" /> <br /> - <div>Changed Values: @string.Join(",", changedItems.Select(i => i.Value))</div> + <div>Changed Values: @string.Join(",", changedValues)</div> <br /><br /><br /><br /> <div>OnSelectItem:</div><br /> <BitDropdown Label="Single select" @@ -381,17 +383,17 @@ DefaultValue="@string.Empty" Placeholder="Select an item" NameSelectors="nameSelectors" - OnSelectItem="(BitDropdownCustom item) => selectedItem1 = item" /> + OnSelectItem="(Product item) => selectedItem1 = item" /> <br /> <div>Selected Value: @selectedItem1?.Value</div> <br /><br /> <BitDropdown Label="Multi select" - IsMultiSelect="true" + MultiSelect Items="GetBasicCustoms()" Placeholder="Select items" DefaultValue="@string.Empty" NameSelectors="nameSelectors" - OnSelectItem="(BitDropdownCustom item) => selectedItem2 = item" /> + OnSelectItem="(Product item) => selectedItem2 = item" /> <br /> <div>Selected Value: @selectedItem2?.Value</div> </div> @@ -405,31 +407,31 @@ <div class="example-content"> <div>With <b>Items</b>:</div><br /> <BitDropdown Label="Single select" + Virtualize Items="virtualizeCustoms1" - NameSelectors="nameSelectors" Placeholder="Select an item" - Virtualize="true" /> + NameSelectors="nameSelectors" /> <br /> <BitDropdown Label="Multi select" + Virtualize + MultiSelect Items="virtualizeCustoms2" - NameSelectors="nameSelectors" - IsMultiSelect="true" Placeholder="Select items" - Virtualize="true" /> + NameSelectors="nameSelectors" /> <br /><br /><br /><br /> <div>With <b>ItemsProvider</b>:</div><br /> <BitDropdown Label="Single select" - Virtualize="true" + Virtualize ItemsProvider="LoadItems" - NameSelectors="nameSelectors" - Placeholder="Select an item" /> + Placeholder="Select an item" + NameSelectors="nameSelectors" /> <br /> <BitDropdown Label="Multi select" - Virtualize="true" - IsMultiSelect="true" + Virtualize + MultiSelect ItemsProvider="LoadItems" - NameSelectors="nameSelectors" - Placeholder="Select items" /> + Placeholder="Select items" + NameSelectors="nameSelectors" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -441,18 +443,19 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample1" Combo - Label="Single select combo box" - Placeholder="Select an option" Items="comboBoxCustoms" + Placeholder="Select an option" + Label="Single select combo box" NameSelectors="comboBoxNameSelectors" /> <br /> <div>Value: @comboBoxValueSample1</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues1" - Combo IsMultiSelect + Combo + MultiSelect + Items="comboBoxCustoms" Label="Multi select combo box" Placeholder="Select an option" - Items="comboBoxCustoms" NameSelectors="comboBoxNameSelectors" /> <br /> <div>Values: @string.Join(',', comboBoxValues1)</div> @@ -467,19 +470,20 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample2" Combo Chips - Label="Single select combo box & chips" - Placeholder="Select an option" Items="comboBoxCustoms" - NameSelectors="comboBoxNameSelectors" /> + Placeholder="Select an option" + NameSelectors="comboBoxNameSelectors" + Label="Single select combo box & chips" /> <br /> <div>Value: @comboBoxValueSample2</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues2" - Combo Chips IsMultiSelect - Label="Multi select combo box & chips" - Placeholder="Select an option" + Combo Chips + MultiSelect Items="comboBoxCustoms" - NameSelectors="comboBoxNameSelectors" /> + Placeholder="Select an option" + NameSelectors="comboBoxNameSelectors" + Label="Multi select combo box & chips" /> <br /> <div>Values: @string.Join(',', comboBoxValues2)</div> </div> @@ -493,12 +497,12 @@ <br /> <BitDropdown @bind-Value="comboBoxValueSample3" Combo Dynamic - Label="Single select combo box & dynamic" - Placeholder="Select an option" Items="comboBoxCustoms" + Placeholder="Select an option" NameSelectors="comboBoxNameSelectors" - DynamicValueGenerator="@((BitDropdownCustom item) => item.Text ?? "")" - OnDynamicAdd="(BitDropdownCustom item) => HandleOnDynamicAdd(item)" /> + Label="Single select combo box & dynamic" + OnDynamicAdd="(Product item) => HandleOnDynamicAdd(item)" + DynamicValueGenerator="@((Product item) => item.Text ?? "")" /> <br /> <div>Value: @comboBoxValueSample3</div> </div> @@ -508,26 +512,27 @@ <div class="example-content"> <br /> <BitDropdown @bind-Value="comboBoxValueSample4" + Responsive Combo Chips Dynamic - Label="Single select combo box, chips & dynamic" - Placeholder="Select an option" Items="comboBoxCustoms" - IsResponsive="true" + Placeholder="Select an option" NameSelectors="comboBoxNameSelectors" - DynamicValueGenerator="@((BitDropdownCustom item) => item.Text ?? "")" - OnDynamicAdd="(BitDropdownCustom item) => HandleOnDynamicAdd(item)" /> + Label="Single select combo box, chips & dynamic" + OnDynamicAdd="(Product item) => HandleOnDynamicAdd(item)" + DynamicValueGenerator="@((Product item) => item.Text ?? "")" /> <br /> <div>Value: @comboBoxValueSample4</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues3" - Combo Chips Dynamic IsMultiSelect - Label="Multi select combo box, chips & dynamic" - Placeholder="Select options" + Responsive + MultiSelect + Combo Chips Dynamic Items="comboBoxCustoms" - IsResponsive="true" + Placeholder="Select options" NameSelectors="comboBoxNameSelectors" - DynamicValueGenerator="@((BitDropdownCustom item) => item.Text ?? "")" - OnDynamicAdd="(BitDropdownCustom item) => HandleOnDynamicAdd(item)" /> + Label="Multi select combo box, chips & dynamic" + OnDynamicAdd="(Product item) => HandleOnDynamicAdd(item)" + DynamicValueGenerator="@((Product item) => item.Text ?? "")" /> <br /> <div>Values: @string.Join(',', comboBoxValues3)</div> </div> @@ -584,17 +589,17 @@ <br /> <div dir="rtl" class="example-content"> <BitDropdown Label="تک انتخابی" + Dir="BitDir.Rtl" Items="GetRtlCustoms()" NameSelectors="nameSelectors" - Placeholder="لطفا انتخاب کنید" - Dir="BitDir.Rtl" /> + Placeholder="لطفا انتخاب کنید" /> <br /><br /> <BitDropdown Label="چند انتخابی" + MultiSelect + Dir="BitDir.Rtl" Items="GetRtlCustoms()" NameSelectors="nameSelectors" - Placeholder="انتخاب چند گزینه ای" - IsMultiSelect="true" - Dir="BitDir.Rtl" /> + Placeholder="انتخاب چند گزینه ای" /> </div> </ExamplePreview> </ComponentExampleBox> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.cs index 77e4fd62cb..7f73fa617e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.cs @@ -6,7 +6,7 @@ public partial class _BitDropdownCustomDemo [Inject] private NavigationManager NavManager { get; set; } = default!; - private BitDropdownNameSelectors<BitDropdownCustom, string> nameSelectors = new() + private BitDropdownNameSelectors<Product, string> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -21,7 +21,7 @@ public partial class _BitDropdownCustomDemo Value = { Selector = c => c.Value }, }; - private BitDropdownNameSelectors<BitDropdownCustom, string> comboBoxNameSelectors = new() + private BitDropdownNameSelectors<Product, string> comboBoxNameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -34,12 +34,12 @@ public partial class _BitDropdownCustomDemo Text = { Selector = c => c.Text }, Title = { Selector = c => c.Title }, Value = { Selector = c => c.Value }, - ValueSetter = (BitDropdownCustom item, string value) => item.Value = value, - TextSetter = (string text, BitDropdownCustom item) => item.Text = text + ValueSetter = (Product item, string value) => item.Value = value, + TextSetter = (string text, Product item) => item.Text = text }; - private List<BitDropdownCustom> GetBasicCustoms() => + private List<Product> GetBasicCustoms() => [ new() { Text = "Fruits", Type = BitDropdownItemType.Header }, new() { Text = "Apple", Value = "f-app" }, @@ -53,7 +53,7 @@ private List<BitDropdownCustom> GetBasicCustoms() => new() { Text = "Lettuce", Value = "v-let" } ]; - private List<BitDropdownCustom> GetDataCustoms() => + private List<Product> GetDataCustoms() => [ new() { Type = BitDropdownItemType.Header, Text = "Items", Payload = new DropdownItemData { IconName = "BulletedList2" } }, new() { Text = "Item a", Value = "A", Payload = new DropdownItemData { IconName = "Memo" } }, @@ -66,10 +66,10 @@ private List<BitDropdownCustom> GetDataCustoms() => new() { Text = "Item f", Value = "F", Payload = new DropdownItemData { IconName = "Running" } } ]; - private ICollection<BitDropdownCustom>? virtualizeCustoms1; - private ICollection<BitDropdownCustom>? virtualizeCustoms2; + private ICollection<Product>? virtualizeCustoms1; + private ICollection<Product>? virtualizeCustoms2; - private List<BitDropdownCustom> GetRtlCustoms() => + private List<Product> GetRtlCustoms() => [ new() { Type = BitDropdownItemType.Header, Text = "میوه ها" }, new() { Text = "سیب", Value = "f-app" }, @@ -82,8 +82,8 @@ private List<BitDropdownCustom> GetRtlCustoms() => new() { Text = "هویج", Value = "v-car" }, new() { Text = "کاهو", Value = "v-let" } ]; - private ICollection<BitDropdownCustom>? dropDirectionCustoms; - private List<BitDropdownCustom> GetStyleClassCustoms() => new() + private ICollection<Product>? dropDirectionCustoms; + private List<Product> GetStyleClassCustoms() => new() { new() { Type = BitDropdownItemType.Header, Text = "Fruits", CssStyle = "text-align: center;" }, new() { Text = "Apple", Value = "f-app", CssClass = "custom-fruit" }, @@ -96,7 +96,7 @@ private List<BitDropdownCustom> GetRtlCustoms() => new() { Text = "Carrot", Value = "v-car", CssClass = "custom-veg" }, new() { Text = "Lettuce", Value = "v-let", CssClass = "custom-veg" } }; - private List<BitDropdownCustom> comboBoxCustoms = new() + private List<Product> comboBoxCustoms = new() { new() { Text = "Fruits", Type = BitDropdownItemType.Header }, new() { Text = "Apple", Value = "f-app" }, @@ -113,16 +113,16 @@ private List<BitDropdownCustom> GetRtlCustoms() => private string controlledValue = "f-app"; - private ICollection<string> controlledValues = ["f-app", "f-ban"]; + private IEnumerable<string> controlledValues = ["f-app", "f-ban"]; - private BitDropdownCustom? changedItem; - private BitDropdownCustom[] changedItems = Array.Empty<BitDropdownCustom>(); + private string? changedValue; + private IEnumerable<string> changedValues = []; - private BitDropdownCustom? selectedItem1; - private BitDropdownCustom? selectedItem2; + private Product? selectedItem1; + private Product? selectedItem2; private string clearValue = "f-app"; - private ICollection<string> clearValues = ["f-app", "f-ban"]; + private IEnumerable<string> clearValues = ["f-app", "f-ban"]; private string successMessage = string.Empty; private FormValidationDropdownModel validationModel = new(); @@ -131,22 +131,22 @@ private List<BitDropdownCustom> GetRtlCustoms() => private string comboBoxValueSample2 = default!; private string comboBoxValueSample3 = default!; private string comboBoxValueSample4 = default!; - private ICollection<string> comboBoxValues1 = []; - private ICollection<string> comboBoxValues2 = []; - private ICollection<string> comboBoxValues3 = []; + private IEnumerable<string> comboBoxValues1 = []; + private IEnumerable<string> comboBoxValues2 = []; + private IEnumerable<string> comboBoxValues3 = []; protected override void OnInitialized() { virtualizeCustoms1 = Enumerable.Range(1, 10_000) - .Select(c => new BitDropdownCustom { Text = $"Category {c}", Value = c.ToString() }) + .Select(p => new Product { Text = $"Produce {p}", Value = p.ToString() }) .ToArray(); virtualizeCustoms2 = Enumerable.Range(1, 10_000) - .Select(c => new BitDropdownCustom { Text = $"Category {c}", Value = c.ToString() }) + .Select(p => new Product { Text = $"Produce {p}", Value = p.ToString() }) .ToArray(); dropDirectionCustoms = Enumerable.Range(1, 15) - .Select(c => new BitDropdownCustom { Value = c.ToString(), Text = $"Category {c}" }) + .Select(p => new Product { Text = $"Produce {p}", Value = p.ToString() }) .ToArray(); base.OnInitialized(); @@ -167,13 +167,13 @@ private void HandleInvalidSubmit() successMessage = string.Empty; } - private void HandleOnDynamicAdd(BitDropdownCustom item) + private void HandleOnDynamicAdd(Product item) { comboBoxCustoms.Add(item); } - private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadItems( - BitDropdownItemsProviderRequest<BitDropdownCustom> request) + private async ValueTask<BitDropdownItemsProviderResult<Product>> LoadItems( + BitDropdownItemsProviderRequest<Product> request) { try { @@ -194,7 +194,7 @@ private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadI var data = await HttpClient.GetFromJsonAsync(url, AppJsonContext.Default.PagedResultProductDto); - var items = data!.Items!.Select(i => new BitDropdownCustom + var items = data!.Items!.Select(i => new Product { Text = i.Name, Value = i.Id.ToString(), @@ -208,7 +208,7 @@ private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadI } catch { - return BitDropdownItemsProviderResult.From(new List<BitDropdownCustom>(), 0); + return BitDropdownItemsProviderResult.From(new List<Product>(), 0); } } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.samples.cs index 1f4bbebc90..fa27dd2ae4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownCustomDemo.razor.samples.cs @@ -9,10 +9,10 @@ public partial class _BitDropdownCustomDemo Placeholder=""Select an item"" /> <BitDropdown Label=""Multi select"" + MultiSelect Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" /> + NameSelectors=""nameSelectors"" /> <BitDropdown Label=""Required"" Required Items=""GetBasicCustoms()"" @@ -22,17 +22,17 @@ public partial class _BitDropdownCustomDemo <BitDropdown Label=""PreserveCalloutWidth"" PreserveCalloutWidth Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" - Placeholder=""Select an item"" /> + Placeholder=""Select an item"" + NameSelectors=""nameSelectors"" /> <BitDropdown Label=""Disabled"" + IsEnabled=""false"" Items=""GetBasicCustoms()"" DefaultValue=""@(""f-ora"")"" - NameSelectors=""nameSelectors"" Placeholder=""Select an item"" - IsEnabled=""false"" />"; + NameSelectors=""nameSelectors"" />"; private readonly string example1CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -45,7 +45,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -59,7 +59,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -100,7 +100,7 @@ public class BitDropdownCustom Placeholder=""Select an item"" IsEnabled=""false"" />"; private readonly string example2CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -113,7 +113,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -127,7 +127,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -141,18 +141,20 @@ public class BitDropdownCustom };"; private readonly string example3RazorCode = @" -<BitDropdown Label=""Single select"" FitWidth +<BitDropdown Label=""Single select"" + FitWidth Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" - Placeholder=""Select an item"" /> + Placeholder=""Select an item"" + NameSelectors=""nameSelectors"" /> -<BitDropdown Label=""Multi select"" FitWidth +<BitDropdown Label=""Multi select"" + FitWidth + MultiSelect Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" />"; + NameSelectors=""nameSelectors"" />"; private readonly string example3CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -165,7 +167,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -179,7 +181,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -195,16 +197,16 @@ public class BitDropdownCustom private readonly string example4RazorCode = @" <BitDropdown NoBorder Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" - Placeholder=""Select an item"" /> + Placeholder=""Select an item"" + NameSelectors=""nameSelectors"" /> <BitDropdown NoBorder + MultiSelect Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" />"; + NameSelectors=""nameSelectors"" />"; private readonly string example4CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -217,7 +219,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -231,7 +233,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -246,12 +248,12 @@ public class BitDropdownCustom private readonly string example5RazorCode = @" <BitDropdown Label=""Responsive Dropdown"" + Responsive Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select an item"" - IsResponsive=true />"; + NameSelectors=""nameSelectors"" />"; private readonly string example5CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -264,7 +266,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -278,7 +280,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -304,16 +306,16 @@ public class BitDropdownCustom Placeholder=""Select an item"" DropDirection=""BitDropDirection.TopAndBottom"" />"; private readonly string example6CsharpCode = @" -private ICollection<BitDropdownCustom>? dropDirectionCustoms; +private ICollection<Product>? dropDirectionCustoms; protected override void OnInitialized() { dropDirectionCustoms = Enumerable.Range(1, 15) - .Select(c => new BitDropdownCustom { Value = c.ToString(), Text = $""Category {c}"" }) + .Select(p => new ProduceModel { Text = $""Produce {p}"", Value = p.ToString() }) .ToArray(); } -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -328,27 +330,26 @@ protected override void OnInitialized() private readonly string example7RazorCode = @" <BitDropdown @bind-Value=""clearValue"" - Label=""Single select dropdown"" + ShowClearButton Items=""GetBasicCustoms()"" NameSelectors=""nameSelectors"" - Placeholder=""Select an option"" - ShowClearButton=""true"" /> + Label=""Single select dropdown"" + Placeholder=""Select an option"" /> <div>Value: @clearValue</div> - <BitDropdown @bind-Values=""clearValues"" - Label=""Multi select dropdown"" + MultiSelect + ShowClearButton Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select options"" - IsMultiSelect=""true"" - ShowClearButton=""true"" /> + Label=""Multi select dropdown"" + NameSelectors=""nameSelectors"" /> <div>Values: @string.Join(',', clearValues)</div>"; private readonly string example7CsharpCode = @" private string? clearValue = ""f-app""; private ICollection<string?> clearValues = new[] { ""f-app"", ""f-ban"" }; -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -361,7 +362,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -375,7 +376,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -389,43 +390,42 @@ public class BitDropdownCustom };"; private readonly string example8RazorCode = @" -<BitDropdown Label=""Single select & auto foucs"" +<BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" + NameSelectors=""nameSelectors"" SearchBoxPlaceholder=""Search item"" /> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" + NameSelectors=""nameSelectors"" SearchBoxPlaceholder=""Search items"" /> - <BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" - SearchBoxPlaceholder=""Search item"" /> + NameSelectors=""nameSelectors"" + SearchBoxPlaceholder=""Search item"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" /> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" - SearchBoxPlaceholder=""Search items"" />"; + NameSelectors=""nameSelectors"" + SearchBoxPlaceholder=""Search items"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" />"; private readonly string example8CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -438,7 +438,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -452,7 +452,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -486,11 +486,11 @@ public class BitDropdownCustom <ValidationMessage For=""@(() => validationModel.Category)"" /> <BitDropdown @bind-Values=""validationModel.Products"" - Label=""Select min 1 and max 2 items"" + MultiSelect Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" /> + NameSelectors=""nameSelectors"" + Label=""Select min 1 and max 2 items"" /> <ValidationMessage For=""@(() => validationModel.Products)"" /> <BitButton ButtonType=""BitButtonType.Submit"">Submit</BitButton> @@ -512,7 +512,7 @@ private async Task HandleValidSubmit() { } private void HandleInvalidSubmit() { } -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -525,7 +525,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -539,7 +539,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string> nameSelectors = new() +private BitDropdownNameSelectors<Product, string> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -659,7 +659,7 @@ public class BitDropdownCustom </CalloutFooterTemplate> </BitDropdown>"; private readonly string example10CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -677,7 +677,7 @@ public class DropdownItemData public string? IconName { get; set; } } -private List<BitDropdownCustom> GetDataCustoms() => new() +private List<Product> GetDataCustoms() => new() { new() { Type = BitDropdownItemType.Header, Text = ""Items"", Payload = new DropdownItemData { IconName = ""BulletedList2"" } }, new() { Text = ""Item a"", Value = ""A"", Payload = new DropdownItemData { IconName = ""Memo"" } }, @@ -690,7 +690,7 @@ public class DropdownItemData new() { Text = ""Item f"", Value = ""F"", Payload = new DropdownItemData { IconName = ""Running"" } } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -712,11 +712,11 @@ public class DropdownItemData <div>Selected Value: @controlledValue</div> <BitDropdown @bind-Values=""controlledValues"" + MultiSelect Label=""Multi select"" Items=""GetBasicCustoms()"" - NameSelectors=""nameSelectors"" Placeholder=""Select items"" - IsMultiSelect=""true"" /> + NameSelectors=""nameSelectors"" /> <div>Selected Values: @string.Join("","", controlledValues)</div> @@ -725,18 +725,18 @@ public class DropdownItemData Items=""GetBasicCustoms()"" Placeholder=""Select an item"" NameSelectors=""nameSelectors"" - TItem=""BitDropdownCustom"" TValue=""string"" - OnValuesChange=""(BitDropdownCustom[] items) => changedItem = items.SingleOrDefault()"" /> -<div>Changed Value: @changedItem?.Value</div> + TItem=""Product"" TValue=""string"" + OnChange=""(string? value) => changedValue = value"" /> +<div>Changed Value: @changedValue</div> <BitDropdown Label=""Multi select"" - IsMultiSelect=""true"" + MultiSelect Items=""GetBasicCustoms()"" Placeholder=""Select items"" NameSelectors=""nameSelectors"" - TItem=""BitDropdownCustom"" TValue=""string"" - OnValuesChange=""(BitDropdownCustom[] items) => changedItems = items"" /> -<div>Changed Values: @string.Join("","", changedItems.Select(i => i.Value))</div> + TItem=""Product"" TValue=""string"" + OnValuesChange=""(IEnumerable<string> values) => changedValues = values"" /> +<div>Changed Values: @string.Join("","", changedValues)</div> @@ -745,28 +745,28 @@ public class DropdownItemData DefaultValue=""@string.Empty"" Placeholder=""Select an item"" NameSelectors=""nameSelectors"" - OnSelectItem=""(BitDropdownCustom item) => selectedItem1 = item"" /> + OnSelectItem=""(Product item) => selectedItem1 = item"" /> <div>Selected Value: @selectedItem1?.Value</div> <BitDropdown Label=""Multi select"" - IsMultiSelect=""true"" + MultiSelect Items=""GetBasicCustoms()"" Placeholder=""Select items"" DefaultValue=""@string.Empty"" NameSelectors=""nameSelectors"" - OnSelectItem=""(BitDropdownCustom item) => selectedItem2 = item"" /> + OnSelectItem=""(Product item) => selectedItem2 = item"" /> <div>Selected Value: @selectedItem2?.Value</div>"; private readonly string example11CsharpCode = @" -private string? controlledValue = ""f-app""; -private ICollection<string?> controlledValues = new[] { ""f-app"", ""f-ban"" }; +private string controlledValue = ""f-app""; +private IEnumerable<string> controlledValues = [""f-app"", ""f-ban""]; -private BitDropdownCustom? changedItem; -private BitDropdownCustom[] changedItems = Array.Empty<BitDropdownCustom>(); +private string? changedValue; +private IEnumerable<string> changedValues = []; -private BitDropdownCustom? selectedItem1; -private BitDropdownCustom? selectedItem2; +private Product? selectedItem1; +private Product? selectedItem2; -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -779,7 +779,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -793,7 +793,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -808,34 +808,33 @@ public class BitDropdownCustom private readonly string example12RazorCode = @" <BitDropdown Label=""Single select"" + Virtualize Items=""virtualizeCustoms1"" - NameSelectors=""nameSelectors"" Placeholder=""Select an item"" - Virtualize=""true"" /> + NameSelectors=""nameSelectors"" /> <BitDropdown Label=""Multi select"" + Virtualize + MultiSelect Items=""virtualizeCustoms2"" - NameSelectors=""nameSelectors"" - IsMultiSelect=""true"" Placeholder=""Select items"" - Virtualize=""true"" /> - + NameSelectors=""nameSelectors"" /> <BitDropdown Label=""Single select"" - Virtualize=""true"" + Virtualize ItemsProvider=""LoadItems"" - NameSelectors=""nameSelectors"" - Placeholder=""Select an item"" /> + Placeholder=""Select an item"" + NameSelectors=""nameSelectors"" /> <BitDropdown Label=""Multi select"" - Virtualize=""true"" - IsMultiSelect=""true"" + Virtualize + MultiSelect ItemsProvider=""LoadItems"" - NameSelectors=""nameSelectors"" - Placeholder=""Select items"" />"; + Placeholder=""Select items"" + NameSelectors=""nameSelectors"" />"; private readonly string example12CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -848,21 +847,21 @@ public class BitDropdownCustom public string? Value { get; set; } } -private ICollection<BitDropdownCustom>? virtualizeCustoms1; -private ICollection<BitDropdownCustom>? virtualizeCustoms2; +private ICollection<Product>? virtualizeCustoms1; +private ICollection<Product>? virtualizeCustoms2; protected override void OnInitialized() { virtualizeCustoms1 = Enumerable.Range(1, 10_000) - .Select(c => new BitDropdownCustom { Text = $""Category {c}"", Value = c.ToString() }) + .Select(p => new Product { Text = $""Produce {p}"", Value = p.ToString() }) .ToArray(); virtualizeCustoms2 = Enumerable.Range(1, 10_000) - .Select(c => new BitDropdownCustom { Text = $""Category {c}"", Value = c.ToString() }) + .Select(p => new Product { Text = $""Produce {p}"", Value = p.ToString() }) .ToArray(); } -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, @@ -875,8 +874,8 @@ protected override void OnInitialized() Value = { Selector = c => c.Value }, }; -private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadItems( - BitDropdownItemsProviderRequest<BitDropdownCustom> request) +private async ValueTask<BitDropdownItemsProviderResult<Product>> LoadItems( + BitDropdownItemsProviderRequest<Product> request) { try { @@ -897,7 +896,7 @@ private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadI var data = await HttpClient.GetFromJsonAsync(url, AppJsonContext.Default.PagedResultProductDto); - var items = data!.Items.Select(i => new BitDropdownCustom + var items = data!.Items.Select(i => new Product { Text = i.Name, Value = i.Id.ToString(), @@ -911,31 +910,32 @@ private async ValueTask<BitDropdownItemsProviderResult<BitDropdownCustom>> LoadI } catch { - return BitDropdownItemsProviderResult.From(new List<BitDropdownCustom>(), 0); + return BitDropdownItemsProviderResult.From(new List<Product>(), 0); } }"; private readonly string example13RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample1"" Combo - Label=""Single select combo box"" - Placeholder=""Select an option"" Items=""comboBoxCustoms"" + Placeholder=""Select an option"" + Label=""Single select combo box"" NameSelectors=""comboBoxNameSelectors"" /> <div>Value: @comboBoxValueSample1</div> <BitDropdown @bind-Values=""comboBoxValues1"" - Combo IsMultiSelect + Combo + MultiSelect + Items=""comboBoxCustoms"" Label=""Multi select combo box"" Placeholder=""Select an option"" - Items=""comboBoxCustoms"" NameSelectors=""comboBoxNameSelectors"" /> <div>Values: @string.Join(',', comboBoxValues1)</div>"; private readonly string example13CsharpCode = @" private string comboBoxValueSample1 = default!; private ICollection<string> comboBoxValues1 = []; -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -948,7 +948,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> comboBoxCustoms = new() +private List<Product> comboBoxCustoms = new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -962,7 +962,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string> comboBoxNameSelectors = new() +private BitDropdownNameSelectors<Product, string> comboBoxNameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -975,31 +975,32 @@ public class BitDropdownCustom Text = { Selector = c => c.Text }, Title = { Selector = c => c.Title }, Value = { Selector = c => c.Value }, - ValueSetter = (BitDropdownCustom item, string value) => item.Value = value, - TextSetter = (string? text, BitDropdownCustom item) => item.Text = text + ValueSetter = (Product item, string value) => item.Value = value, + TextSetter = (string? text, Product item) => item.Text = text };"; private readonly string example14RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample2"" Combo Chips - Label=""Single select combo box & chips"" - Placeholder=""Select an option"" Items=""comboBoxCustoms"" - NameSelectors=""comboBoxNameSelectors"" /> + Placeholder=""Select an option"" + NameSelectors=""comboBoxNameSelectors"" + Label=""Single select combo box & chips"" /> <div>Value: @comboBoxValueSample2</div> <BitDropdown @bind-Values=""comboBoxValues2"" - Combo Chips IsMultiSelect - Label=""Multi select combo box & chips"" - Placeholder=""Select an option"" + Combo Chips + MultiSelect Items=""comboBoxCustoms"" - NameSelectors=""comboBoxNameSelectors"" /> + Placeholder=""Select an option"" + NameSelectors=""comboBoxNameSelectors"" + Label=""Multi select combo box & chips"" /> <div>Values: @string.Join(',', comboBoxValues2)</div>"; private readonly string example14CsharpCode = @" private string comboBoxValueSample2 = default!; private ICollection<string> comboBoxValues2 = []; -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -1012,7 +1013,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> comboBoxCustoms = new() +private List<Product> comboBoxCustoms = new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -1026,7 +1027,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string> comboBoxNameSelectors = new() +private BitDropdownNameSelectors<Product, string> comboBoxNameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -1039,53 +1040,54 @@ public class BitDropdownCustom Text = { Selector = c => c.Text }, Title = { Selector = c => c.Title }, Value = { Selector = c => c.Value }, - ValueSetter = (BitDropdownCustom item, string value) => item.Value = value, - TextSetter = (string? text, BitDropdownCustom item) => item.Text = text + ValueSetter = (Product item, string value) => item.Value = value, + TextSetter = (string? text, Product item) => item.Text = text };"; private readonly string example15RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample3"" Combo Dynamic - Label=""Single select combo box & dynamic"" - Placeholder=""Select an option"" Items=""comboBoxCustoms"" + Placeholder=""Select an option"" NameSelectors=""comboBoxNameSelectors"" - DynamicValueGenerator=""@((BitDropdownCustom item) => item.Text ?? """")"" - OnDynamicAdd=""(BitDropdownCustom item) => HandleOnDynamicAdd(item)"" /> + Label=""Single select combo box & dynamic"" + OnDynamicAdd=""(Product item) => HandleOnDynamicAdd(item)"" + DynamicValueGenerator=""@((Product item) => item.Text ?? """")"" /> <div>Value: @comboBoxValueSample3</div> <BitDropdown @bind-Value=""comboBoxValueSample4"" + Responsive Combo Chips Dynamic - Label=""Single select combo box, chips & dynamic"" - Placeholder=""Select an option"" Items=""comboBoxCustoms"" - IsResponsive=""true"" + Placeholder=""Select an option"" NameSelectors=""comboBoxNameSelectors"" - DynamicValueGenerator=""@((BitDropdownCustom item) => item.Text ?? """")"" - OnDynamicAdd=""(BitDropdownCustom item) => HandleOnDynamicAdd(item)"" /> + Label=""Single select combo box, chips & dynamic"" + OnDynamicAdd=""(Product item) => HandleOnDynamicAdd(item)"" + DynamicValueGenerator=""@((Product item) => item.Text ?? """")"" /> <div>Value: @comboBoxValueSample4</div> <BitDropdown @bind-Values=""comboBoxValues3"" - Combo Chips Dynamic IsMultiSelect - Label=""Multi select combo box, chips & dynamic"" - Placeholder=""Select options"" + Responsive + MultiSelect + Combo Chips Dynamic Items=""comboBoxCustoms"" - IsResponsive=""true"" + Placeholder=""Select options"" NameSelectors=""comboBoxNameSelectors"" - DynamicValueGenerator=""@((BitDropdownCustom item) => item.Text ?? """")"" - OnDynamicAdd=""(BitDropdownCustom item) => HandleOnDynamicAdd(item)"" /> + Label=""Multi select combo box, chips & dynamic"" + OnDynamicAdd=""(Product item) => HandleOnDynamicAdd(item)"" + DynamicValueGenerator=""@((Product item) => item.Text ?? """")"" /> <div>Values: @string.Join(',', comboBoxValues3)</div>"; private readonly string example15CsharpCode = @" private string comboBoxValueSample3 = default!; private string comboBoxValueSample4 = default!; private ICollection<string> comboBoxValues3 = []; -private void HandleOnDynamicAdd(BitDropdownCustom item) +private void HandleOnDynamicAdd(Product item) { comboBoxCustoms.Add(item); } -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -1098,7 +1100,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> comboBoxCustoms = new() +private List<Product> comboBoxCustoms = new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -1112,7 +1114,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string> comboBoxNameSelectors = new() +private BitDropdownNameSelectors<Product, string> comboBoxNameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -1125,8 +1127,8 @@ public class BitDropdownCustom Text = { Selector = c => c.Text }, Title = { Selector = c => c.Title }, Value = { Selector = c => c.Value }, - ValueSetter = (BitDropdownCustom item, string value) => item.Value = value, - TextSetter = (string? text, BitDropdownCustom item) => item.Text = text + ValueSetter = (Product item, string value) => item.Value = value, + TextSetter = (string? text, Product item) => item.Text = text };"; private readonly string example16RazorCode = @" @@ -1208,7 +1210,7 @@ public class BitDropdownCustom ItemButton = ""custom-item-button"", ScrollContainer = ""custom-scroll-container"" })"" />"; private readonly string example16CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? CssClass { get; set; } @@ -1223,7 +1225,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetBasicCustoms() => new() +private List<Product> GetBasicCustoms() => new() { new() { Text = ""Fruits"", Type = BitDropdownItemType.Header }, new() { Text = ""Apple"", Value = ""f-app"" }, @@ -1237,7 +1239,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"" } }; -private List<BitDropdownCustom> GetStyleClassCustoms() => new() +private List<Product> GetStyleClassCustoms() => new() { new() { Type = BitDropdownItemType.Header, Text = ""Fruits"", CssStyle = ""text-align: center;"" }, new() { Text = ""Apple"", Value = ""f-app"", CssClass = ""custom-fruit"" }, @@ -1251,7 +1253,7 @@ public class BitDropdownCustom new() { Text = ""Lettuce"", Value = ""v-let"", CssClass = ""custom-veg"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Class = { Selector = c => c.CssClass }, @@ -1268,19 +1270,19 @@ public class BitDropdownCustom private readonly string example17RazorCode = @" <BitDropdown Label=""تک انتخابی"" + Dir=""BitDir.Rtl"" Items=""GetRtlCustoms()"" NameSelectors=""nameSelectors"" - Placeholder=""لطفا انتخاب کنید"" - Dir=""BitDir.Rtl"" /> + Placeholder=""لطفا انتخاب کنید"" /> <BitDropdown Label=""چند انتخابی"" + MultiSelect + Dir=""BitDir.Rtl"" Items=""GetRtlCustoms()"" NameSelectors=""nameSelectors"" - Placeholder=""انتخاب چند گزینه ای"" - IsMultiSelect=""true"" - Dir=""BitDir.Rtl"" />"; + Placeholder=""انتخاب چند گزینه ای"" />"; private readonly string example17CsharpCode = @" -public class BitDropdownCustom +public class Product { public string? Label { get; set; } public string? Key { get; set; } @@ -1293,7 +1295,7 @@ public class BitDropdownCustom public string? Value { get; set; } } -private List<BitDropdownCustom> GetRtlCustoms() => new() +private List<Product> GetRtlCustoms() => new() { new() { Type = BitDropdownItemType.Header, Text = ""میوه ها"" }, new() { Text = ""سیب"", Value = ""f-app"" }, @@ -1307,7 +1309,7 @@ public class BitDropdownCustom new() { Text = ""کاهو"", Value = ""v-let"" } }; -private BitDropdownNameSelectors<BitDropdownCustom, string?> nameSelectors = new() +private BitDropdownNameSelectors<Product, string?> nameSelectors = new() { AriaLabel = { Selector = c => c.Label }, Id = { Selector = c => c.Key }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor index 73c6f879d8..708eea8e85 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor @@ -9,10 +9,10 @@ TItem="BitDropdownItem<string>" TValue="string" /> <br /><br /> <BitDropdown Label="Multi select" - Items="GetBasicItems()" + MultiSelect DefaultValue="@("")" - Placeholder="Select items" - IsMultiSelect="true" /> + Items="GetBasicItems()" + Placeholder="Select items" /> <br /><br /> <BitDropdown Label="Required" Required Items="GetBasicItems()" @@ -26,10 +26,10 @@ Placeholder="Select an item" /> <br /><br /> <BitDropdown Label="Disabled" + IsEnabled="false" Items="GetBasicItems()" DefaultValue="@("f-ora")" - Placeholder="Select an item" - IsEnabled="false" /> + Placeholder="Select an item" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -77,16 +77,18 @@ <ExamplePreview> <div>Enables fit-content width for the dropdown that sets the width of the component based on its content size.</div> <br /><br /> - <BitDropdown Label="Single select" FitWidth + <BitDropdown Label="Single select" + FitWidth Items="GetBasicItems()" DefaultValue="@string.Empty" Placeholder="Select an item" /> <br /><br /> - <BitDropdown Label="Multi select" FitWidth + <BitDropdown Label="Multi select" + FitWidth + MultiSelect Items="GetBasicItems()" - DefaultValue="@string.Empty" Placeholder="Select items" - IsMultiSelect="true" /> + DefaultValue="@string.Empty" /> </ExamplePreview> </ComponentExampleBox> @@ -101,10 +103,10 @@ Placeholder="Select an item" /> <br /><br /> <BitDropdown NoBorder + MultiSelect Items="GetBasicItems()" - DefaultValue="@string.Empty" Placeholder="Select items" - IsMultiSelect="true" /> + DefaultValue="@string.Empty" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -115,10 +117,10 @@ <br /> <div class="example-content"> <BitDropdown Label="Responsive Dropdown" + Responsive Items="GetBasicItems()" DefaultValue="@string.Empty" - Placeholder="Select an item" - IsResponsive=true /> + Placeholder="Select an item" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -149,19 +151,19 @@ <br /> <div class="example-content"> <BitDropdown @bind-Value="clearValue" - Label="Single select dropdown" + ShowClearButton Items="GetBasicItems()" - Placeholder="Select an option" - ShowClearButton="true" /> + Label="Single select dropdown" + Placeholder="Select an option" /> <br /> <div>Value: @clearValue</div> <br><br /><br /> <BitDropdown @bind-Values="clearValues" - Label="Multi select dropdown" + MultiSelect + ShowClearButton Items="GetBasicItems()" Placeholder="Select options" - IsMultiSelect="true" - ShowClearButton="true" /> + Label="Multi select dropdown" /> <br /> <div>Values: @string.Join(',', clearValues)</div> </div> @@ -174,39 +176,39 @@ <br /><br /> <div class="example-content"> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Items="GetBasicItems()" DefaultValue="@string.Empty" Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" SearchBoxPlaceholder="Search item" /> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Items="GetBasicItems()" - DefaultValue="@string.Empty" Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" + DefaultValue="@string.Empty" SearchBoxPlaceholder="Search items" /> <br /><br /><br /><br /> <div><b>Custom search function</b>:</div><br /> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Items="GetBasicItems()" DefaultValue="@string.Empty" Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" - SearchBoxPlaceholder="Search item" /> + SearchBoxPlaceholder="Search item" + SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" /> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Items="GetBasicItems()" - DefaultValue="@string.Empty" Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" - SearchBoxPlaceholder="Search items" /> + DefaultValue="@string.Empty" + SearchBoxPlaceholder="Search items" + SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -231,10 +233,10 @@ <br /> <BitDropdown @bind-Values="validationModel.Products" - Label="Select min 1 and max 2 items" + MultiSelect Items="GetBasicItems()" Placeholder="Select items" - IsMultiSelect="true" /> + Label="Select min 1 and max 2 items" /> <ValidationMessage For="@(() => validationModel.Products)" /> @@ -345,10 +347,10 @@ <div>Selected Value: @controlledValue</div> <br /><br /> <BitDropdown @bind-Values="controlledValues" + MultiSelect Label="Multi select" Items="GetBasicItems()" - Placeholder="Select items" - IsMultiSelect="true" /> + Placeholder="Select items" /> <br /> <div>Selected Values: @string.Join(",", controlledValues)</div> <br /><br /><br /><br /> @@ -357,18 +359,18 @@ Items="GetBasicItems()" Placeholder="Select an item" TItem="BitDropdownItem<string>" TValue="string" - OnValuesChange="(BitDropdownItem<string>[] items) => changedItem = items.SingleOrDefault()" /> + OnChange="(string value) => changedValue = value" /> <br /> - <div>Changed Value: @changedItem?.Value</div> + <div>Changed Value: @changedValue</div> <br /><br /> <BitDropdown Label="Multi select" - IsMultiSelect="true" + MultiSelect Items="GetBasicItems()" Placeholder="Select items" TItem="BitDropdownItem<string>" TValue="string" - OnValuesChange="(BitDropdownItem<string>[] items) => changedItems = items" /> + OnValuesChange="(IEnumerable<string> values) => changedValues = values" /> <br /> - <div>Changed Values: @string.Join(",", changedItems.Select(i => i.Value))</div> + <div>Changed Values: @string.Join(",", changedValues)</div> <br /><br /><br /><br /> <div><b>OnSelectItem</b>:</div><br /> <BitDropdown Label="Single select" @@ -380,10 +382,10 @@ <div>Selected Value: @selectedItem1?.Value</div> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect Items="GetBasicItems()" - DefaultValue="@string.Empty" Placeholder="Select items" - IsMultiSelect="true" + DefaultValue="@string.Empty" OnSelectItem="(BitDropdownItem<string> item) => selectedItem2 = item" /> <br /> <div>Selected Value: @selectedItem2?.Value</div> @@ -398,28 +400,28 @@ <div class="example-content"> <div>With <b>Items</b>:</div><br /> <BitDropdown Label="Single select" + Virtualize Items="virtualizeItems1" DefaultValue="@string.Empty" - Placeholder="Select an item" - Virtualize="true" /> + Placeholder="Select an item" /> <br /> <BitDropdown Label="Multi select" + Virtualize + MultiSelect Items="virtualizeItems2" - DefaultValue="@string.Empty" - IsMultiSelect="true" Placeholder="Select items" - Virtualize="true" /> + DefaultValue="@string.Empty" /> <br /><br /><br /><br /> <div>With <b>ItemsProvider</b>:</div><br /> <BitDropdown Label="Single select" - Virtualize="true" + Virtualize ItemsProvider="LoadItems" Placeholder="Select an item" TItem="BitDropdownItem<string>" TValue="string" /> <br /> <BitDropdown Label="Multi select" - Virtualize="true" - IsMultiSelect="true" + Virtualize + MultiSelect ItemsProvider="LoadItems" Placeholder="Select items" TItem="BitDropdownItem<string>" TValue="string" /> @@ -434,17 +436,18 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample1" Combo - Label="Single select combo box" + Items="comboBoxItems" Placeholder="Select an option" - Items="comboBoxItems" /> + Label="Single select combo box" /> <br /> <div>Value: @comboBoxValueSample1</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues1" - Combo IsMultiSelect + Combo + MultiSelect + Items="comboBoxItems" Label="Multi select combo box" - Placeholder="Select an option" - Items="comboBoxItems" /> + Placeholder="Select an option" /> <br /> <div>Values: @string.Join(',', comboBoxValues1)</div> </div> @@ -458,17 +461,18 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample2" Combo Chips - Label="Single select combo box & chips" + Items="comboBoxItems" Placeholder="Select an option" - Items="comboBoxItems" /> + Label="Single select combo box & chips" /> <br /> <div>Value: @comboBoxValueSample2</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues2" - Combo Chips IsMultiSelect - Label="Multi select combo box & chips" + Combo Chips + MultiSelect + Items="comboBoxItems" Placeholder="Select an option" - Items="comboBoxItems" /> + Label="Multi select combo box & chips" /> <br /> <div>Values: @string.Join(',', comboBoxValues2)</div> </div> @@ -482,9 +486,9 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample3" Combo Dynamic - Label="Single select combo box & dynamic" - Placeholder="Select an option" Items="comboBoxItems" + Placeholder="Select an option" + Label="Single select combo box & dynamic" DynamicValueGenerator="(BitDropdownItem<string> item) => item.Text" OnDynamicAdd="(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)" /> <br /> @@ -496,23 +500,23 @@ <div class="example-content"> <br /> <BitDropdown @bind-Value="comboBoxValueSample4" + Responsive Combo Chips Dynamic - Label="Single select combo box, chips & dynamic" - Placeholder="Select an option" Items="comboBoxItems" - IsResponsive="true" + Placeholder="Select an option" + Label="Single select combo box, chips & dynamic" DynamicValueGenerator="(BitDropdownItem<string> item) => item.Text" OnDynamicAdd="(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)" /> <br /> <div>Value: @comboBoxValueSample4</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues3" + Responsive + MultiSelect Combo Chips Dynamic - Label="Multi select combo box, chips & dynamic" - Placeholder="Select options" Items="comboBoxItems" - IsMultiSelect="true" - IsResponsive="true" + Placeholder="Select options" + Label="Multi select combo box, chips & dynamic" DynamicValueGenerator="(BitDropdownItem<string> item) => item.Text" OnDynamicAdd="(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)" /> <br /> @@ -532,10 +536,10 @@ Placeholder="Select an item" Style="margin: 1rem; box-shadow: aqua 0 0 0.5rem; text-shadow: aqua 0 0 0.5rem;" /> <br /> - <BitDropdown Items="GetBasicItems()" + <BitDropdown Class="custom-class" + Items="GetBasicItems()" DefaultValue="@string.Empty" - Placeholder="Select an item" - Class="custom-class" /> + Placeholder="Select an item" /> <br /><br /><br /><br /> <div>Item's Style & Class:</div><br /> <BitDropdown Items="GetStyleClassItems()" @@ -577,11 +581,11 @@ Dir="BitDir.Rtl" /> <br /><br /> <BitDropdown Label="چند انتخابی" + MultiSelect + Dir="BitDir.Rtl" Items="GetRtlItems()" DefaultValue="@string.Empty" - Placeholder="انتخاب چند گزینه ای" - IsMultiSelect="true" - Dir="BitDir.Rtl" /> + Placeholder="انتخاب چند گزینه ای" /> </div> </ExamplePreview> </ComponentExampleBox> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.cs index 5edae632c1..57eff57c9d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.cs @@ -82,16 +82,16 @@ private List<BitDropdownItem<string>> GetStyleClassItems() => private string controlledValue = "f-app"; - private ICollection<string> controlledValues = ["f-app", "f-ban"]; + private IEnumerable<string> controlledValues = ["f-app", "f-ban"]; - private BitDropdownItem<string>? changedItem; - private BitDropdownItem<string>[] changedItems = Array.Empty<BitDropdownItem<string>>(); + private string? changedValue; + private IEnumerable<string> changedValues = []; private BitDropdownItem<string>? selectedItem1; private BitDropdownItem<string>? selectedItem2; private string? clearValue = "f-app"; - private ICollection<string?> clearValues = ["f-app", "f-ban"]; + private IEnumerable<string?> clearValues = ["f-app", "f-ban"]; private string successMessage = string.Empty; private FormValidationDropdownModel validationModel = new(); @@ -100,9 +100,9 @@ private List<BitDropdownItem<string>> GetStyleClassItems() => private string comboBoxValueSample2 = default!; private string comboBoxValueSample3 = default!; private string comboBoxValueSample4 = default!; - private ICollection<string> comboBoxValues1 = []; - private ICollection<string> comboBoxValues2 = []; - private ICollection<string> comboBoxValues3 = []; + private IEnumerable<string> comboBoxValues1 = []; + private IEnumerable<string> comboBoxValues2 = []; + private IEnumerable<string> comboBoxValues3 = []; protected override void OnInitialized() { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.samples.cs index 37a6c71265..713f58a769 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownItemDemo.razor.samples.cs @@ -9,10 +9,10 @@ public partial class _BitDropdownItemDemo TItem=""BitDropdownItem<string>"" TValue=""string"" /> <BitDropdown Label=""Multi select"" - Items=""GetBasicItems()"" + MultiSelect DefaultValue=""@("""")"" - Placeholder=""Select items"" - IsMultiSelect=""true"" /> + Items=""GetBasicItems()"" + Placeholder=""Select items"" /> <BitDropdown Label=""Required"" Required Items=""GetBasicItems()"" @@ -26,10 +26,10 @@ public partial class _BitDropdownItemDemo Placeholder=""Select an item"" /> <BitDropdown Label=""Disabled"" + IsEnabled=""false"" Items=""GetBasicItems()"" DefaultValue=""@(""f-ora"")"" - Placeholder=""Select an item"" - IsEnabled=""false"" />"; + Placeholder=""Select an item"" />"; private readonly string example1CsharpCode = @" private List<BitDropdownItem<string>> GetBasicItems() => new() { @@ -92,16 +92,18 @@ public partial class _BitDropdownItemDemo };"; private readonly string example3RazorCode = @" -<BitDropdown Label=""Single select"" FitWidth +<BitDropdown Label=""Single select"" + FitWidth Items=""GetBasicItems()"" DefaultValue=""@string.Empty"" Placeholder=""Select an item"" /> -<BitDropdown Label=""Multi select"" FitWidth +<BitDropdown Label=""Multi select"" + FitWidth + MultiSelect Items=""GetBasicItems()"" - DefaultValue=""@string.Empty"" Placeholder=""Select items"" - IsMultiSelect=""true"" />"; + DefaultValue=""@string.Empty"" />"; private readonly string example3CsharpCode = @" private List<BitDropdownItem<string>> GetBasicItems() => new() { @@ -124,10 +126,10 @@ public partial class _BitDropdownItemDemo Placeholder=""Select an item"" /> <BitDropdown NoBorder + MultiSelect Items=""GetBasicItems()"" - DefaultValue=""@string.Empty"" Placeholder=""Select items"" - IsMultiSelect=""true"" />"; + DefaultValue=""@string.Empty"" />"; private readonly string example4CsharpCode = @" private List<BitDropdownItem<string>> GetBasicItems() => new() { @@ -145,10 +147,10 @@ public partial class _BitDropdownItemDemo private readonly string example5RazorCode = @" <BitDropdown Label=""Responsive Dropdown"" + Responsive Items=""GetBasicItems()"" DefaultValue=""@string.Empty"" - Placeholder=""Select an item"" - IsResponsive=true />"; + Placeholder=""Select an item"" />"; private readonly string example5CsharpCode = @" private List<BitDropdownItem<string>> GetBasicItems() => new() { @@ -188,19 +190,18 @@ protected override void OnInitialized() private readonly string example7RazorCode = @" <BitDropdown @bind-Value=""clearValue"" - Label=""Single select dropdown"" + ShowClearButton Items=""GetBasicItems()"" - Placeholder=""Select an option"" - ShowClearButton=""true"" /> + Label=""Single select dropdown"" + Placeholder=""Select an option"" /> <div>Value: @clearValue</div> - <BitDropdown @bind-Values=""clearValues"" - Label=""Multi select dropdown"" + MultiSelect + ShowClearButton Items=""GetBasicItems()"" Placeholder=""Select options"" - IsMultiSelect=""true"" - ShowClearButton=""true"" /> + Label=""Multi select dropdown"" /> <div>Values: @string.Join(',', clearValues)</div>"; private readonly string example7CsharpCode = @" private string? clearValue = ""f-app""; @@ -222,40 +223,39 @@ protected override void OnInitialized() private readonly string example8RazorCode = @" <BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Items=""GetBasicItems()"" DefaultValue=""@string.Empty"" Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" SearchBoxPlaceholder=""Search item"" /> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Items=""GetBasicItems()"" - DefaultValue=""@string.Empty"" Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" + DefaultValue=""@string.Empty"" SearchBoxPlaceholder=""Search items"" /> - <BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Items=""GetBasicItems()"" DefaultValue=""@string.Empty"" Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" - SearchBoxPlaceholder=""Search item"" /> + SearchBoxPlaceholder=""Search item"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" /> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Items=""GetBasicItems()"" - DefaultValue=""@string.Empty"" Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" - SearchBoxPlaceholder=""Search items"" />"; + DefaultValue=""@string.Empty"" + SearchBoxPlaceholder=""Search items"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" />"; private readonly string example8CsharpCode = @" private List<BitDropdownItem<string>> GetBasicItems() => new() { @@ -291,10 +291,10 @@ protected override void OnInitialized() <ValidationMessage For=""@(() => validationModel.Category)"" /> <BitDropdown @bind-Values=""validationModel.Products"" - Label=""Select min 1 and max 2 items"" + MultiSelect Items=""GetBasicItems()"" Placeholder=""Select items"" - IsMultiSelect=""true"" /> + Label=""Select min 1 and max 2 items"" /> <ValidationMessage For=""@(() => validationModel.Products)"" /> <BitButton ButtonType=""BitButtonType.Submit"">Submit</BitButton> @@ -463,10 +463,10 @@ public class DropdownItemData <div>Selected Value: @controlledValue</div> <BitDropdown @bind-Values=""controlledValues"" + MultiSelect Label=""Multi select"" Items=""GetBasicItems()"" - Placeholder=""Select items"" - IsMultiSelect=""true"" /> + Placeholder=""Select items"" /> <div>Selected Values: @string.Join("","", controlledValues)</div> @@ -475,16 +475,16 @@ public class DropdownItemData Items=""GetBasicItems()"" Placeholder=""Select an item"" TItem=""BitDropdownItem<string>"" TValue=""string"" - OnValuesChange=""(BitDropdownItem<string>[] items) => changedItem = items.SingleOrDefault()"" /> -<div>Changed Value: @changedItem?.Value</div> + OnChange=""(string value) => changedValue = value"" /> +<div>Changed Value: @changedValue</div> <BitDropdown Label=""Multi select"" - IsMultiSelect=""true"" + MultiSelect Items=""GetBasicItems()"" Placeholder=""Select items"" TItem=""BitDropdownItem<string>"" TValue=""string"" - OnValuesChange=""(BitDropdownItem<string>[] items) => changedItems = items"" /> -<div>Changed Values: @string.Join("","", changedItems.Select(i => i.Value))</div> + OnValuesChange=""(IEnumerable<string> values) => changedValues = values"" /> +<div>Changed Values: @string.Join("","", changedValues)</div> @@ -496,18 +496,18 @@ public class DropdownItemData <div>Selected Value: @selectedItem1?.Value</div> <BitDropdown Label=""Multi select"" + MultiSelect Items=""GetBasicItems()"" - DefaultValue=""@string.Empty"" Placeholder=""Select items"" - IsMultiSelect=""true"" + DefaultValue=""@string.Empty"" OnSelectItem=""(BitDropdownItem<string> item) => selectedItem2 = item"" /> <div>Selected Value: @selectedItem2?.Value</div>"; private readonly string example11CsharpCode = @" private string controlledValue = ""f-app""; -private ICollection<string?> controlledValues = new[] { ""f-app"", ""f-ban"" }; +private IEnumerable<string> controlledValues = [""f-app"", ""f-ban""]; -private BitDropdownItem<string>? changedItem; -private BitDropdownItem<string>[] changedItems = Array.Empty<BitDropdownItem<string>>(); +private string? changedValue; +private IEnumerable<string> changedValues = []; private BitDropdownItem<string>? selectedItem1; private BitDropdownItem<string>? selectedItem2; @@ -528,29 +528,28 @@ public class DropdownItemData private readonly string example12RazorCode = @" <BitDropdown Label=""Single select"" + Virtualize Items=""virtualizeItems1"" DefaultValue=""@string.Empty"" - Placeholder=""Select an item"" - Virtualize=""true"" /> + Placeholder=""Select an item"" /> <BitDropdown Label=""Multi select"" + Virtualize + MultiSelect Items=""virtualizeItems2"" - DefaultValue=""@string.Empty"" - IsMultiSelect=""true"" Placeholder=""Select items"" - Virtualize=""true"" /> - + DefaultValue=""@string.Empty"" /> <BitDropdown Label=""Single select"" - Virtualize=""true"" + Virtualize ItemsProvider=""LoadItems"" Placeholder=""Select an item"" TItem=""BitDropdownItem<string>"" TValue=""string"" /> <BitDropdown Label=""Multi select"" - Virtualize=""true"" - IsMultiSelect=""true"" + Virtualize + MultiSelect ItemsProvider=""LoadItems"" Placeholder=""Select items"" TItem=""BitDropdownItem<string>"" TValue=""string"" />"; @@ -612,16 +611,17 @@ private async ValueTask<BitDropdownItemsProviderResult<BitDropdownItem<string>>> private readonly string example13RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample1"" Combo - Label=""Single select combo box"" + Items=""comboBoxItems"" Placeholder=""Select an option"" - Items=""comboBoxItems"" /> + Label=""Single select combo box"" /> <div>Value: @comboBoxValueSample1</div> <BitDropdown @bind-Values=""comboBoxValues1"" - Combo IsMultiSelect + Combo + MultiSelect + Items=""comboBoxItems"" Label=""Multi select combo box"" - Placeholder=""Select an option"" - Items=""comboBoxItems"" /> + Placeholder=""Select an option"" /> <div>Values: @string.Join(',', comboBoxValues1)</div>"; private readonly string example13CsharpCode = @" private string comboBoxValueSample1 = default!; @@ -644,16 +644,17 @@ Combo IsMultiSelect private readonly string example14RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample2"" Combo Chips - Label=""Single select combo box & chips"" + Items=""comboBoxItems"" Placeholder=""Select an option"" - Items=""comboBoxItems"" /> + Label=""Single select combo box & chips"" /> <div>Value: @comboBoxValueSample2</div> <BitDropdown @bind-Values=""comboBoxValues2"" - Combo Chips IsMultiSelect - Label=""Multi select combo box & chips"" + Combo Chips + MultiSelect + Items=""comboBoxItems"" Placeholder=""Select an option"" - Items=""comboBoxItems"" /> + Label=""Multi select combo box & chips"" /> <div>Values: @string.Join(',', comboBoxValues2)</div>"; private readonly string example14CsharpCode = @" private string comboBoxValueSample2 = default!; @@ -676,30 +677,30 @@ Combo Chips IsMultiSelect private readonly string example15RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample3"" Combo Dynamic - Label=""Single select combo box & dynamic"" - Placeholder=""Select an option"" Items=""comboBoxItems"" + Placeholder=""Select an option"" + Label=""Single select combo box & dynamic"" DynamicValueGenerator=""(BitDropdownItem<string> item) => item.Text"" OnDynamicAdd=""(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)"" /> <div>Value: @comboBoxValueSample3</div> <BitDropdown @bind-Value=""comboBoxValueSample4"" + Responsive Combo Chips Dynamic - Label=""Single select combo box, chips & dynamic"" - Placeholder=""Select an option"" Items=""comboBoxItems"" - IsResponsive=""true"" + Placeholder=""Select an option"" + Label=""Single select combo box, chips & dynamic"" DynamicValueGenerator=""(BitDropdownItem<string> item) => item.Text"" OnDynamicAdd=""(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)"" /> <div>Value: @comboBoxValueSample4</div> <BitDropdown @bind-Values=""comboBoxValues3"" + Responsive + MultiSelect Combo Chips Dynamic - Label=""Multi select combo box, chips & dynamic"" - Placeholder=""Select options"" Items=""comboBoxItems"" - IsMultiSelect=""true"" - IsResponsive=""true"" + Placeholder=""Select options"" + Label=""Multi select combo box, chips & dynamic"" DynamicValueGenerator=""(BitDropdownItem<string> item) => item.Text"" OnDynamicAdd=""(BitDropdownItem<string> item) => HandleOnDynamicAdd(item)"" /> <div>Values: @string.Join(',', comboBoxValues3)</div>"; @@ -771,10 +772,10 @@ Combo Chips Dynamic Placeholder=""Select an item"" Style=""margin: 1rem; box-shadow: aqua 0 0 0.5rem; text-shadow: aqua 0 0 0.5rem;"" /> -<BitDropdown Items=""GetBasicItems()"" +<BitDropdown Class=""custom-class"" + Items=""GetBasicItems()"" DefaultValue=""@string.Empty"" - Placeholder=""Select an item"" - Class=""custom-class"" /> + Placeholder=""Select an item"" /> <BitDropdown Items=""GetStyleClassItems()"" @@ -837,11 +838,11 @@ Combo Chips Dynamic Dir=""BitDir.Rtl"" /> <BitDropdown Label=""چند انتخابی"" + MultiSelect + Dir=""BitDir.Rtl"" Items=""GetRtlItems()"" DefaultValue=""@string.Empty"" - Placeholder=""انتخاب چند گزینه ای"" - IsMultiSelect=""true"" - Dir=""BitDir.Rtl"" />"; + Placeholder=""انتخاب چند گزینه ای"" />"; private readonly string example17CsharpCode = @" private List<BitDropdownItem<string>> GetRtlItems() => new() { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor index 3c5aff939b..b36d90ffe7 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor @@ -19,8 +19,9 @@ </BitDropdown> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect Placeholder="Select items" - IsMultiSelect="true" TItem="BitDropdownOption<string>" TValue="string"> + TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> @@ -128,9 +129,10 @@ } </BitDropdown> <br /><br /> - <BitDropdown Label="Multi select" FitWidth + <BitDropdown Label="Multi select" + FitWidth + MultiSelect Placeholder="Select items" - IsMultiSelect="true" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -155,8 +157,8 @@ </BitDropdown> <br /><br /> <BitDropdown NoBorder + MultiSelect Placeholder="Select items" - IsMultiSelect="true" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -173,8 +175,8 @@ <br /> <div class="example-content"> <BitDropdown Label="Responsive Dropdown" + Responsive Placeholder="Select an item" - IsResponsive=true TItem="BitDropdownOption<string>" TValue="string"> <BitDropdownOption ItemType="BitDropdownItemType.Header" Text="Fruits" Value="@string.Empty" /> <BitDropdownOption Text="Apple" Value="@("f-app")" /> @@ -225,9 +227,9 @@ <br /> <div class="example-content"> <BitDropdown @bind-Value="clearValue" + ShowClearButton Label="Single select dropdown" Placeholder="Select an option" - ShowClearButton="true" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -239,10 +241,10 @@ <div>Value: @clearValue</div> <br /><br /><br /> <BitDropdown @bind-Values="clearValues" - Label="Multi select dropdown" + MultiSelect + ShowClearButton Placeholder="Select options" - IsMultiSelect="true" - ShowClearButton="true" + Label="Multi select dropdown" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -261,9 +263,9 @@ <br /><br /> <div class="example-content"> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" SearchBoxPlaceholder="Search item" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) @@ -273,9 +275,9 @@ </BitDropdown> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" SearchBoxPlaceholder="Search items" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) @@ -286,12 +288,12 @@ <br /><br /><br /><br /> <div><b>Custom search function</b>:</div><br /> <BitDropdown Label="Single select & auto focus" + ShowSearchBox + AutoFocusSearchBox Placeholder="Select an item" - ShowSearchBox="true" - AutoFocusSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" SearchBoxPlaceholder="Search item" - TItem="BitDropdownOption<string>" TValue="string"> + TItem="BitDropdownOption<string>" TValue="string" + SearchFunction="(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"> @foreach (var item in basicItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> @@ -299,12 +301,12 @@ </BitDropdown> <br /><br /> <BitDropdown Label="Multi select" + MultiSelect + ShowSearchBox Placeholder="Select items" - IsMultiSelect="true" - ShowSearchBox="true" - SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()" SearchBoxPlaceholder="Search items" - TItem="BitDropdownOption<string>" TValue="string"> + TItem="BitDropdownOption<string>" TValue="string" + SearchFunction="(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"> @foreach (var item in basicItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> @@ -339,9 +341,9 @@ <br /> <BitDropdown @bind-Values="validationModel.Products" - Label="Select min 1 and max 2 items" + MultiSelect Placeholder="Select items" - IsMultiSelect="true" + Label="Select min 1 and max 2 items" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -498,9 +500,9 @@ <div>Selected Value: @controlledValue</div> <br /><br /> <BitDropdown @bind-Values="controlledValues" + MultiSelect Label="Multi select" Placeholder="Select items" - IsMultiSelect="true" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in basicItems) { @@ -514,27 +516,27 @@ <BitDropdown Label="Single select" Placeholder="Select an item" TItem="BitDropdownOption<string>" TValue="string" - OnValuesChange="(BitDropdownOption<string>[] items) => changedItem = items.SingleOrDefault()"> + OnChange="(string value) => changedValue = value"> @foreach (var item in basicItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> } </BitDropdown> <br /> - <div>Changed Value: @changedItem?.Value</div> + <div>Changed Value: @changedValue</div> <br /><br /> <BitDropdown Label="Multi select" - IsMultiSelect="true" + MultiSelect Placeholder="Select items" TItem="BitDropdownOption<string>" TValue="string" - OnValuesChange="(BitDropdownOption<string>[] items) => changedItems = items"> + OnValuesChange="(IEnumerable<string> values) => changedValues = values"> @foreach (var item in basicItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> } </BitDropdown> <br /> - <div>Changed Values: @string.Join(",", changedItems.Select(i => i.Value))</div> + <div>Changed Values: @string.Join(",", changedValues)</div> <br /><br /><br /><br /> <div>OnSelectItem:</div><br /> <BitDropdown Label="Single select" @@ -551,9 +553,9 @@ <div>Selected Value: @selectedItem1?.Value</div> <br /><br /> <BitDropdown Label="Multi select" - IsMultiSelect="true" - DefaultValue="@string.Empty" + MultiSelect Placeholder="Select items" + DefaultValue="@string.Empty" TItem="BitDropdownOption<string>" TValue="string" OnSelectItem="(BitDropdownOption<string> item) => selectedItem2 = item"> @foreach (var item in basicItems) @@ -586,7 +588,7 @@ <div>Value: @comboBoxValueSample1</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues1" - Combo IsMultiSelect + Combo MultiSelect Label="Multi select combo box" Placeholder="Select an option" TItem="BitDropdownOption<string>" TValue="string"> @@ -608,8 +610,8 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample2" Combo Chips - Label="Single select combo box & chips" Placeholder="Select an option" + Label="Single select combo box & chips" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in comboBoxItems) { @@ -620,9 +622,9 @@ <div>Value: @comboBoxValueSample2</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues2" - Combo Chips IsMultiSelect - Label="Multi select combo box & chips" + Combo Chips MultiSelect Placeholder="Select an option" + Label="Multi select combo box & chips" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in comboBoxItems) { @@ -642,8 +644,8 @@ <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample3" Combo Dynamic - Label="Single select combo box & dynamic" Placeholder="Select an option" + Label="Single select combo box & dynamic" DynamicValueGenerator="(BitDropdownOption<string> item) => item.Text" OnDynamicAdd="(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)" TItem="BitDropdownOption<string>" TValue="string"> @@ -660,13 +662,13 @@ <br /> <div class="example-content"> <BitDropdown @bind-Value="comboBoxValueSample4" + Responsive Combo Chips Dynamic - Label="Single select combo box, chips & dynamic" Placeholder="Select an option" - IsResponsive="true" + Label="Single select combo box, chips & dynamic" + TItem="BitDropdownOption<string>" TValue="string" DynamicValueGenerator="(BitDropdownOption<string> item) => item.Text" - OnDynamicAdd="(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)" - TItem="BitDropdownOption<string>" TValue="string"> + OnDynamicAdd="(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)"> @foreach (var item in comboBoxItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> @@ -676,14 +678,14 @@ <div>Value: @comboBoxValueSample4</div> <br /><br /> <BitDropdown @bind-Values="comboBoxValues3" + Responsive + MultiSelect Combo Chips Dynamic - Label="Multi select combo box, chips & dynamic" Placeholder="Select options" - IsMultiSelect="true" - IsResponsive="true" + Label="Multi select combo box, chips & dynamic" + TItem="BitDropdownOption<string>" TValue="string" DynamicValueGenerator="(BitDropdownOption<string> item) => item.Text" - OnDynamicAdd="(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)" - TItem="BitDropdownOption<string>" TValue="string"> + OnDynamicAdd="(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)"> @foreach (var item in comboBoxItems) { <BitDropdownOption ItemType="item.ItemType" Text="@item.Text" Value="item.Value" IsEnabled="item.IsEnabled" /> @@ -765,8 +767,8 @@ <br /> <div dir="rtl" class="example-content"> <BitDropdown Label="تک انتخابی" - Placeholder="لطفا انتخاب کنید" Dir="BitDir.Rtl" + Placeholder="لطفا انتخاب کنید" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in rtlItems) { @@ -775,9 +777,9 @@ </BitDropdown> <br /><br /> <BitDropdown Label="چند انتخابی" - Placeholder="انتخاب چند گزینه ای" - IsMultiSelect="true" + MultiSelect Dir="BitDir.Rtl" + Placeholder="انتخاب چند گزینه ای" TItem="BitDropdownOption<string>" TValue="string"> @foreach (var item in rtlItems) { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.cs index 3d21c3107f..43d7a57ed8 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.cs @@ -70,16 +70,16 @@ public partial class _BitDropdownOptionDemo private string controlledValue = "f-app"; - private ICollection<string?> controlledValues = ["f-app", "f-ban"]; + private IEnumerable<string?> controlledValues = ["f-app", "f-ban"]; - private BitDropdownOption<string>? changedItem; - private BitDropdownOption<string>[] changedItems = Array.Empty<BitDropdownOption<string>>(); + private string? changedValue; + private IEnumerable<string> changedValues = []; private BitDropdownOption<string>? selectedItem1; private BitDropdownOption<string>? selectedItem2; private string? clearValue = "f-app"; - private ICollection<string?> clearValues = ["f-app", "f-ban"]; + private IEnumerable<string?> clearValues = ["f-app", "f-ban"]; private string successMessage = string.Empty; private FormValidationDropdownModel validationModel = new(); @@ -88,9 +88,9 @@ public partial class _BitDropdownOptionDemo private string comboBoxValueSample2 = default!; private string comboBoxValueSample3 = default!; private string comboBoxValueSample4 = default!; - private ICollection<string> comboBoxValues1 = []; - private ICollection<string> comboBoxValues2 = []; - private ICollection<string> comboBoxValues3 = []; + private IEnumerable<string> comboBoxValues1 = []; + private IEnumerable<string> comboBoxValues2 = []; + private IEnumerable<string> comboBoxValues3 = []; protected override void OnInitialized() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.samples.cs index 5855fd0f1d..1123e58844 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Dropdown/_BitDropdownOptionDemo.razor.samples.cs @@ -19,8 +19,9 @@ public partial class _BitDropdownOptionDemo </BitDropdown> <BitDropdown Label=""Multi select"" + MultiSelect Placeholder=""Select items"" - IsMultiSelect=""true"" TItem=""BitDropdownOption<string>"" TValue=""string""> + TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -143,9 +144,10 @@ public partial class _BitDropdownOptionDemo } </BitDropdown> -<BitDropdown Label=""Multi select"" FitWidth +<BitDropdown Label=""Multi select"" + FitWidth + MultiSelect Placeholder=""Select items"" - IsMultiSelect=""true"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -178,8 +180,8 @@ public partial class _BitDropdownOptionDemo </BitDropdown> <BitDropdown NoBorder + MultiSelect Placeholder=""Select items"" - IsMultiSelect=""true"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -203,8 +205,8 @@ public partial class _BitDropdownOptionDemo private readonly string example5RazorCode = @" <BitDropdown Label=""Responsive Dropdown"" + Responsive Placeholder=""Select an item"" - IsResponsive=true TItem=""BitDropdownOption<string>"" TValue=""string""> <BitDropdownOption ItemType=""BitDropdownItemType.Header"" Text=""Fruits"" Value=""@string.Empty"" /> <BitDropdownOption Text=""Apple"" Value=""@(""f-app"")"" /> @@ -250,9 +252,9 @@ protected override void OnInitialized() private readonly string example7RazorCode = @" <BitDropdown @bind-Value=""clearValue"" + ShowClearButton Label=""Single select dropdown"" Placeholder=""Select an option"" - ShowClearButton=""true"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -263,10 +265,10 @@ protected override void OnInitialized() <BitDropdown @bind-Values=""clearValues"" - Label=""Multi select dropdown"" + MultiSelect + ShowClearButton Placeholder=""Select options"" - IsMultiSelect=""true"" - ShowClearButton=""true"" + Label=""Multi select dropdown"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -294,9 +296,9 @@ protected override void OnInitialized() private readonly string example8RazorCode = @" <BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" SearchBoxPlaceholder=""Search item"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) @@ -306,9 +308,9 @@ protected override void OnInitialized() </BitDropdown> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" SearchBoxPlaceholder=""Search items"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) @@ -320,12 +322,12 @@ protected override void OnInitialized() <BitDropdown Label=""Single select & auto focus"" + ShowSearchBox + AutoFocusSearchBox Placeholder=""Select an item"" - ShowSearchBox=""true"" - AutoFocusSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" SearchBoxPlaceholder=""Search item"" - TItem=""BitDropdownOption<string>"" TValue=""string""> + TItem=""BitDropdownOption<string>"" TValue=""string"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.StartsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()""> @foreach (var item in basicItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -333,12 +335,12 @@ protected override void OnInitialized() </BitDropdown> <BitDropdown Label=""Multi select"" + MultiSelect + ShowSearchBox Placeholder=""Select items"" - IsMultiSelect=""true"" - ShowSearchBox=""true"" - SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()"" SearchBoxPlaceholder=""Search items"" - TItem=""BitDropdownOption<string>"" TValue=""string""> + TItem=""BitDropdownOption<string>"" TValue=""string"" + SearchFunction=""(items, text) => items.Where(i => i.Text?.EndsWith(text, StringComparison.OrdinalIgnoreCase) ?? false).ToArray()""> @foreach (var item in basicItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -384,9 +386,9 @@ protected override void OnInitialized() <ValidationMessage For=""@(() => validationModel.Category)"" /> <BitDropdown @bind-Values=""validationModel.Products"" - Label=""Select min 1 and max 2 items"" + MultiSelect Placeholder=""Select items"" - IsMultiSelect=""true"" + Label=""Select min 1 and max 2 items"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -596,9 +598,9 @@ private void HandleInvalidSubmit() { } <div>Selected Value: @controlledValue</div> <BitDropdown @bind-Values=""controlledValues"" + MultiSelect Label=""Multi select"" Placeholder=""Select items"" - IsMultiSelect=""true"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in basicItems) { @@ -612,25 +614,25 @@ private void HandleInvalidSubmit() { } <BitDropdown Label=""Single select"" Placeholder=""Select an item"" TItem=""BitDropdownOption<string>"" TValue=""string"" - OnValuesChange=""(BitDropdownOption<string>[] items) => changedItem = items.SingleOrDefault()""> + OnChange=""(string value) => changedValue = value""> @foreach (var item in basicItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> } </BitDropdown> -<div>Changed Value: @changedItem?.Value</div> +<div>Changed Value: @changedValue</div> <BitDropdown Label=""Multi select"" - IsMultiSelect=""true"" + MultiSelect Placeholder=""Select items"" TItem=""BitDropdownOption<string>"" TValue=""string"" - OnValuesChange=""(BitDropdownOption<string>[] items) => changedItems = items""> + OnValuesChange=""(IEnumerable<string> values) => changedValues = values""> @foreach (var item in basicItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> } </BitDropdown> -<div>Changed Values: @string.Join("","", changedItems.Select(i => i.Value))</div> +<div>Changed Values: @string.Join("","", changedValues)</div> @@ -647,9 +649,9 @@ private void HandleInvalidSubmit() { } <div>Selected Value: @selectedItem1?.Value</div> <BitDropdown Label=""Multi select"" - IsMultiSelect=""true"" - DefaultValue=""@string.Empty"" + MultiSelect Placeholder=""Select items"" + DefaultValue=""@string.Empty"" TItem=""BitDropdownOption<string>"" TValue=""string"" OnSelectItem=""(BitDropdownOption<string> item) => selectedItem2 = item""> @foreach (var item in basicItems) @@ -660,10 +662,10 @@ private void HandleInvalidSubmit() { } <div>Selected Value: @selectedItem2?.Value</div>"; private readonly string example11CsharpCode = @" private string controlledValue = ""f-app""; -private ICollection<string?> controlledValues = new[] { ""f-app"", ""f-ban"" }; +private IEnumerable<string?> controlledValues = [""f-app"", ""f-ban""]; -private BitDropdownOption<string>? changedItem; -private BitDropdownOption<string>[] changedItems = Array.Empty<BitDropdownOption<string>>(); +private string? changedValue; +private IEnumerable<string> changedValues = []; private BitDropdownOption<string>? selectedItem1; private BitDropdownOption<string>? selectedItem2; @@ -697,7 +699,8 @@ private void HandleInvalidSubmit() { } <div>Value: @comboBoxValueSample1</div> <br /><br /> <BitDropdown @bind-Values=""comboBoxValues1"" - Combo IsMultiSelect + Combo + MultiSelect Label=""Multi select combo box"" Placeholder=""Select an option"" TItem=""BitDropdownOption<string>"" TValue=""string""> @@ -741,9 +744,10 @@ Combo Chips <div>Value: @comboBoxValueSample2</div> <br /><br /> <BitDropdown @bind-Values=""comboBoxValues2"" - Combo Chips IsMultiSelect - Label=""Multi select combo box & chips"" + Combo Chips + MultiSelect Placeholder=""Select an option"" + Label=""Multi select combo box & chips"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in comboBoxItems) { @@ -773,11 +777,11 @@ Combo Chips IsMultiSelect private readonly string example14RazorCode = @" <BitDropdown @bind-Value=""comboBoxValueSample3"" Combo Dynamic - Label=""Single select combo box & dynamic"" Placeholder=""Select an option"" + Label=""Single select combo box & dynamic"" + TItem=""BitDropdownOption<string>"" TValue=""string"" DynamicValueGenerator=""(BitDropdownOption<string> item) => item.Text"" - OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)"" - TItem=""BitDropdownOption<string>"" TValue=""string""> + OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)""> @foreach (var item in comboBoxItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -786,13 +790,13 @@ Combo Dynamic <div>Value: @comboBoxValueSample3</div> <BitDropdown @bind-Value=""comboBoxValueSample4"" + Responsive Combo Chips Dynamic - Label=""Single select combo box, chips & dynamic"" Placeholder=""Select an option"" - IsResponsive=""true"" + Label=""Single select combo box, chips & dynamic"" + TItem=""BitDropdownOption<string>"" TValue=""string"" DynamicValueGenerator=""(BitDropdownOption<string> item) => item.Text"" - OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)"" - TItem=""BitDropdownOption<string>"" TValue=""string""> + OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)""> @foreach (var item in comboBoxItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -801,14 +805,14 @@ Combo Chips Dynamic <div>Value: @comboBoxValueSample4</div> <BitDropdown @bind-Values=""comboBoxValues3"" + Responsive + MultiSelect Combo Chips Dynamic - Label=""Multi select combo box, chips & dynamic"" Placeholder=""Select options"" - IsMultiSelect=""true"" - IsResponsive=""true"" + Label=""Multi select combo box, chips & dynamic"" + TItem=""BitDropdownOption<string>"" TValue=""string"" DynamicValueGenerator=""(BitDropdownOption<string> item) => item.Text"" - OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)"" - TItem=""BitDropdownOption<string>"" TValue=""string""> + OnDynamicAdd=""(BitDropdownOption<string> item) => HandleOnDynamicAdd(item)""> @foreach (var item in comboBoxItems) { <BitDropdownOption ItemType=""item.ItemType"" Text=""@item.Text"" Value=""item.Value"" IsEnabled=""item.IsEnabled"" /> @@ -968,8 +972,8 @@ private void HandleOnDynamicAdd(BitDropdownOption<string> item) private readonly string example16RazorCode = @" <BitDropdown Label=""تک انتخابی"" - Placeholder=""لطفا انتخاب کنید"" Dir=""BitDir.Rtl"" + Placeholder=""لطفا انتخاب کنید"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in rtlItems) { @@ -978,9 +982,9 @@ private void HandleOnDynamicAdd(BitDropdownOption<string> item) </BitDropdown> <BitDropdown Label=""چند انتخابی"" - Placeholder=""انتخاب چند گزینه ای"" - IsMultiSelect=""true"" + MultiSelect Dir=""BitDir.Rtl"" + Placeholder=""انتخاب چند گزینه ای"" TItem=""BitDropdownOption<string>"" TValue=""string""> @foreach (var item in rtlItems) { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor index 9e1d775ffa..eaaa4c4182 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor @@ -13,212 +13,184 @@ <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <div class="example-desc">Files can be uploaded automatically after selecting them.</div> - <div> - <BitFileUpload Label="Select or drag and drop files" UploadUrl="@ChunkedUploadUrl" MaxSize="1024 * 1024 * 500" /> - </div> + <div>Files can be uploaded automatically after selecting them.</div><br /> + <BitFileUpload Label="Select or drag and drop files" UploadUrl="@UploadUrl" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Auto & Multiple" RazorCode="@example2RazorCode" CsharpCode="@example1CsharpCode" Id="example2"> + <ComponentExampleBox Title="Auto & Multiple" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div class="example-desc">Multiple files can be selected to upload automatically.</div> - <div> - <BitFileUpload IsMultiSelect="true" - AutoUploadEnabled="true" - Label="Select or drag and drop files" - UploadUrl="@ChunkedUploadUrl" - MaxSize="1024 * 1024 * 500" /> - </div> + <div>Multiple files can be selected to upload automatically.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + AutoUpload + MultiSelect + UploadUrl="@UploadUrl" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="MaxSize" RazorCode="@example3RazorCode" CsharpCode="@example1CsharpCode" Id="example3"> + <ComponentExampleBox Title="MaxSize" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div class="example-desc">Multiple files can be selected to upload automatically with limited size.</div> - <div> - <BitFileUpload IsMultiSelect="true" - AutoUploadEnabled="true" - MaxSize="1024 * 1024 * 100" - Label="Select or drag and drop files" - UploadUrl="@ChunkedUploadUrl" /> - </div> + <div>Multiple files can be selected to upload automatically with limited size.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + AutoUpload + MultiSelect + UploadUrl="@UploadUrl" + MaxSize="1024 * 1024 * 1" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="AllowedExtensions" RazorCode="@example4RazorCode" CsharpCode="@example1CsharpCode" Id="example4"> + <ComponentExampleBox Title="AllowedExtensions" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> - <div class="example-desc">Single or multiple file uploading limited only by file extensions, which in this case the allowed file extensions include gif, jpg and mp4.</div> - <div> - <BitFileUpload IsMultiSelect="true" - AutoUploadEnabled="false" - AllowedExtensions="@(new List<string> { ".gif",".jpg",".mp4" })" - Label="Select or drag and drop files" - UploadUrl="@ChunkedUploadUrl" - MaxSize="1024 * 1024 * 500" /> - </div> + <div>Single or multiple file uploading limited only by file extensions, which in this case the allowed file extensions include gif, jpg and mp4.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + MultiSelect + UploadUrl="@UploadUrl" + AllowedExtensions="@(new List<string> { ".gif",".jpg",".mp4" })" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Removable" RazorCode="@example5RazorCode" CsharpCode="@example2CsharpCode" Id="example5"> + <ComponentExampleBox Title="Removable" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-desc">Single or multiple file uploading with remove functionality enabled.</div> - <div> - <BitFileUpload IsMultiSelect="true" - Label="Select or drag and drop files" - UploadUrl="@ChunkedUploadUrl" - RemoveUrl="@RemoveUrl" - ShowRemoveButton="true" - MaxSize="1024 * 1024 * 500" /> - </div> + <div>Single or multiple file uploading with remove functionality enabled.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + MultiSelect + ShowRemoveButton + UploadUrl="@UploadUrl" + RemoveUrl="@RemoveUrl" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Events" RazorCode="@example6RazorCode" CsharpCode="@example1CsharpCode" Id="example6"> + <ComponentExampleBox Title="Events" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> - <div class="example-desc">Multiple files can be selected to upload automatically with calling all upload complete event.</div> - <div> - <BitFileUpload IsMultiSelect="true" - AutoUploadEnabled="true" - MaxSize="1024 * 1024 * 500" - UploadUrl="@ChunkedUploadUrl" - Label="Select or drag and drop files" - OnAllUploadsComplete="@(() => onAllUploadsCompleteText = "All File Uploaded")" - OnUploading="@(info => info.HttpHeaders = new Dictionary<string, string> { {"key1", "value1"} })" /> - </div> - <div class="example-desc">@onAllUploadsCompleteText</div> + <div>Multiple files can be selected to upload automatically with calling all upload complete event.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + AutoUpload + MultiSelect + UploadUrl="@UploadUrl" + OnAllUploadsComplete="@(() => onAllUploadsCompleteText = "All File Uploaded")" + OnUploading="@(info => info.HttpHeaders = new Dictionary<string, string> { {"key1", "value1"} })" /> + <div>@onAllUploadsCompleteText</div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Configuring http request" RazorCode="@example7RazorCode" CsharpCode="@example1CsharpCode" Id="example7"> + <ComponentExampleBox Title="Configuring http request" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <div class="example-desc">Multiple files can be selected to upload and remove with custom http headers and query strings.</div> - <div> - <BitFileUpload IsMultiSelect="true" - Label="Select or drag and drop files" - UploadUrl="@ChunkedUploadUrl" - UploadRequestHttpHeaders="@(new Dictionary<string, string>{ {"header1", "value1" } })" - UploadRequestQueryStrings="@(new Dictionary<string, string>{ {"qs1", "qsValue1" } })" - RemoveUrl="@RemoveUrl" - RemoveRequestHttpHeaders="@(new Dictionary<string, string>{ {"header2", "value2" } })" - RemoveRequestQueryStrings="@(new Dictionary<string, string>{ {"qs2", "qsValue2" } })" - MaxSize="1024 * 1024 * 500" /> - </div> + <div>Multiple files can be selected to upload and remove with custom http headers and query strings.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + MultiSelect + UploadUrl="@UploadUrl" + RemoveUrl="@RemoveUrl" + UploadRequestQueryStrings="@(new Dictionary<string, string>{ {"qs1", "qsValue1" } })" + UploadRequestHttpHeaders="@(new Dictionary<string, string>{ {"header1", "value1" } })" + RemoveRequestQueryStrings="@(new Dictionary<string, string>{ {"qs2", "qsValue2" } })" + RemoveRequestHttpHeaders="@(new Dictionary<string, string>{ {"header2", "value2" } })" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Non-Chunked" RazorCode="@example8RazorCode" CsharpCode="@example1CsharpCode" Id="example8"> + <ComponentExampleBox Title="Chunked" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> - <div class="example-desc">Files can be uploaded automatically after selecting them.</div> - <div> - <BitFileUpload Label="Select or drag and drop files" - ChunkedUploadEnabled="false" - UploadUrl="@NonChunkedUploadUrl" - MaxSize="1024 * 1024 * 500" /> - </div> + <div>Files can be uploaded in chunks.</div><br /> + <BitFileUpload Label="Select or drag and drop files" + ChunkedUpload + UploadUrl="@ChunkedUploadUrl" /> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Templates" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> <ExamplePreview> - <div class="example-desc">Use custom template for file upload.</div> - <div> - <BitFileUpload @ref="bitFileUpload" - Label="" - UploadUrl="@NonChunkedUploadUrl" - RemoveUrl="@RemoveUrl" - MaxSize="1024 * 1024 * 2" - AllowedExtensions="@(new List<string> { ".jpeg", ".jpg", ".png", ".bpm" })" - SuccessfulUploadMessage="File upload succeeded" - NotAllowedExtensionErrorMessage="File type not supported"> - <LabelTemplate> - @if (FileUploadIsEmpty()) - { - <div class="browse-file"> - <div class="browse-file-header"> - <i class="bit-icon bit-icon--CloudUpload" /> - <div> - Drag and drop or - </div> - <div> - <strong> - Browse file - </strong> - </div> + <div>Use custom template for file upload.</div><br /> + <BitFileUpload @ref="bitFileUpload" + Label="" + UploadUrl="@UploadUrl" + RemoveUrl="@RemoveUrl" + MaxSize="1024 * 1024 * 2" + SuccessfulUploadMessage="File upload succeeded" + NotAllowedExtensionErrorMessage="File type not supported" + AllowedExtensions="@(new List<string> { ".jpeg", ".jpg", ".png", ".bpm" })"> + <LabelTemplate> + @if (FileUploadIsEmpty()) + { + <div class="browse-file"> + <div class="browse-file-header"> + <i class="bit-icon bit-icon--CloudUpload" /> + <div> + Drag and drop or </div> + <div> + <strong> + Browse file + </strong> + </div> + </div> - <div class="browse-file-footer"> - <div> - Max file size: 2 MB - </div> - <div> - Supported file types: jpg, jpeg, png, bpm - </div> + <div class="browse-file-footer"> + <div> + Max file size: 2 MB + </div> + <div> + Supported file types: jpg, jpeg, png, bpm </div> </div> - } - </LabelTemplate> - <FileViewTemplate Context="file"> - @if (file.Status != BitFileUploadStatus.Removed) - { - <div class="file-list"> - <div class="file-info"> - <div class="file-info-ico"> - <i class="bit-icon bit-icon--FileImage" /> - </div> - <div class="file-info-data"> - <div class="file-info-title"> - <div class="file-info-name">@file.Name</div> - <div class="file-info-btns"> - <label for="@bitFileUpload.InputId"><i class="bit-icon bit-icon--CloudUpload upload-ico" /></label> - <i class="bit-icon bit-icon--ChromeClose remove-ico" @onclick="HandleRemoveOnClick" /> - </div> + </div> + } + </LabelTemplate> + <FileViewTemplate Context="file"> + @if (file.Status != BitFileUploadStatus.Removed) + { + <div class="file-list"> + <div class="file-info"> + <div class="file-info-ico"> + <i class="bit-icon bit-icon--FileImage" /> + </div> + <div class="file-info-data"> + <div class="file-info-title"> + <div class="file-info-name">@file.Name</div> + <div class="file-info-btns"> + <label for="@bitFileUpload.InputId"><i class="bit-icon bit-icon--CloudUpload upload-ico" /></label> + <i class="bit-icon bit-icon--ChromeClose remove-ico" @onclick="HandleRemoveOnClick" /> </div> - @if (file.Status is BitFileUploadStatus.InProgress or BitFileUploadStatus.Pending) - { - var fileUploadPercent = GetFileUploadPercent(file); - <div class="file-info-subtitle">@GetFileUploadSize(file) - @fileUploadPercent%</div> - <div class="file-info-progressbar-container"> - <div class="file-info-progressbar" role="progressbar" style="width:@fileUploadPercent%;" aria-valuemin="0" aria-valuemax="100" aria-valuenow="@fileUploadPercent"></div> - </div> - } - else - { - <div class="@(file.Status == BitFileUploadStatus.Completed ? "file-info-s-msg" : "file-info-e-msg")">@GetUploadMessageStr(file)</div> - } </div> + @if (file.Status is BitFileUploadStatus.InProgress or BitFileUploadStatus.Pending) + { + var fileUploadPercent = GetFileUploadPercent(file); + <div class="file-info-subtitle">@GetFileUploadSize(file) - @fileUploadPercent%</div> + <div class="file-info-progressbar-container"> + <div class="file-info-progressbar" role="progressbar" style="width:@fileUploadPercent%;" aria-valuemin="0" aria-valuemax="100" aria-valuenow="@fileUploadPercent"></div> + </div> + } + else + { + <div class="@(file.Status == BitFileUploadStatus.Completed ? "file-info-s-msg" : "file-info-e-msg")">@GetUploadMessageStr(file)</div> + } </div> + </div> - <div class="file-list-footer"> - <div> - Max file size: 2 MB - </div> - <div> - Supported file types: jpg, jpeg, png, bpm - </div> + <div class="file-list-footer"> + <div> + Max file size: 2 MB + </div> + <div> + Supported file types: jpg, jpeg, png, bpm </div> </div> - } - </FileViewTemplate> - </BitFileUpload> - <br /> - <BitButton OnClick="HandleUploadOnClick">Upload</BitButton> - </div> + </div> + } + </FileViewTemplate> + </BitFileUpload> + <br /> + <BitButton OnClick="HandleUploadOnClick">Upload</BitButton> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Public API" RazorCode="@example10RazorCode" CsharpCode="@example10CsharpCode" Id="example10"> <ExamplePreview> - <div class="example-desc">Use a custom method for the open file selection dialog.</div> - <div> - <BitFileUpload @ref="bitFileUploadWithBrowseFile" - Label="" - UploadUrl="@NonChunkedUploadUrl" - RemoveUrl="@RemoveUrl" - MaxSize="1024 * 1024 * 500" /> - <br /> - <BitButton OnClick="HandleBrowseFileOnClick">Browse file</BitButton> - </div> + <div>Use a custom method for the open file selection dialog.</div><br /> + <BitFileUpload @ref="bitFileUploadWithBrowseFile" + Label="" + UploadUrl="@UploadUrl" + RemoveUrl="@RemoveUrl" /> + <br /> + <BitButton OnClick="HandleBrowseFileOnClick">Browse file</BitButton> </ExamplePreview> </ComponentExampleBox> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs index e323b50fda..ddeaecff2f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs @@ -15,28 +15,28 @@ public partial class BitFileUploadDemo { Name = "AllowedExtensions", Type = "IReadOnlyCollection<string>", - DefaultValue = "new List<string> { \"*\" }", + DefaultValue = "[\"*\"]", Description = "Filters files by extension.", }, new() { - Name = "AutoChunkSizeEnabled", + Name = "AutoChunkSize", Type = "bool", DefaultValue = "false", Description = "Calculate the chunk size dynamically based on the user's Internet speed between 512 KB and 10 MB." }, new() { - Name = "AutoUploadEnabled", + Name = "AutoUpload", Type = "bool", DefaultValue = "false", Description = "Automatically starts the upload file(s) process immediately after selecting the file(s)." }, new() { - Name = "ChunkedUploadEnabled", + Name = "ChunkedUpload", Type = "bool", - DefaultValue = "true", + DefaultValue = "false", Description = "Enables or disables the chunked upload feature." }, new() @@ -48,7 +48,7 @@ public partial class BitFileUploadDemo }, new() { - Name = "IsMultiSelect", + Name = "MultiSelect", Type = "bool", DefaultValue = "false", Description = "Enables multi-file select & upload." @@ -92,49 +92,65 @@ public partial class BitFileUploadDemo { Name = "OnAllUploadsComplete", Type = "EventCallback<BitFileInfo[]>", - Description = "Callback for when all files are uploaded." + Description = "Callback for when all files are uploaded.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnChange", Type = "EventCallback<BitFileInfo[]>", - Description = "Callback for when file or files status change." + Description = "Callback for when file or files status change.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnProgress", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when the file upload is progressed." + Description = "Callback for when the file upload is progressed.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnRemoveComplete", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when a remove file is done." + Description = "Callback for when a remove file is done.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnRemoveFailed", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when a remove file is failed." + Description = "Callback for when a remove file is failed.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnUploading", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when a file upload is about to start." + Description = "Callback for when a file upload is about to start.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnUploadComplete", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when a file upload is done." + Description = "Callback for when a file upload is done.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { Name = "OnUploadFailed", Type = "EventCallback<BitFileInfo>", - Description = "Callback for when an upload file is failed." + Description = "Callback for when an upload file is failed.", + LinkType = LinkType.Link, + Href = "#file-info" }, new() { @@ -212,10 +228,10 @@ public partial class BitFileUploadDemo [ new() { - Id = "nav-class-styles", + Id = "file-info", Title = "BitFileInfo", - Parameters = new() - { + Parameters = + [ new() { Name = "ContentType", @@ -275,7 +291,7 @@ public partial class BitFileUploadDemo DefaultValue = "Pending", Description = "The status of the file in the BitFileUpload.", LinkType = LinkType.Link, - Href = "#uploadstatus-enum" + Href = "#upload-status-enum" }, new() { @@ -284,7 +300,7 @@ public partial class BitFileUploadDemo DefaultValue = "null", Description = "The HTTP header at upload file." } - } + ] } ]; @@ -292,7 +308,7 @@ public partial class BitFileUploadDemo [ new() { - Id = "uploadstatus-enum", + Id = "upload-status-enum", Name = "BitFileUploadStatus", Description = "", Items = @@ -361,8 +377,8 @@ public partial class BitFileUploadDemo [Inject] private IConfiguration _configuration { get; set; } = default!; private string onAllUploadsCompleteText = "No File"; + private string UploadUrl => $"{_configuration.GetApiServerAddress()}FileUpload/UploadNonChunkedFile"; private string ChunkedUploadUrl => $"{_configuration.GetApiServerAddress()}FileUpload/UploadChunkedFile"; - private string NonChunkedUploadUrl => $"{_configuration.GetApiServerAddress()}FileUpload/UploadNonChunkedFile"; private string RemoveUrl => $"{_configuration.GetApiServerAddress()}FileUpload/RemoveFile"; private BitFileUpload bitFileUpload = default!; @@ -440,63 +456,77 @@ private async Task HandleBrowseFileOnClick() private readonly string example1RazorCode = @" -<BitFileUpload Label=""Select or drag and drop files"" UploadUrl=""@ChunkedUploadUrl"" MaxSize=""1024 * 1024 * 500"" />"; +<BitFileUpload Label=""Select or drag and drop files"" UploadUrl=""@UploadUrl"" />"; private readonly string example1CsharpCode = @" private string UploadUrl = $""/Upload"";"; private readonly string example2RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - AutoUploadEnabled=""true"" - Label=""Select or drag and drop files"" +<BitFileUpload Label=""Select or drag and drop files"" + AutoUpload + MultiSelect UploadUrl=""@UploadUrl"" />"; private readonly string example2CsharpCode = @" -private string UploadUrl = $""/Upload""; -private string RemoveUrl = $""/Remove"";"; +private string UploadUrl = $""/Upload"";"; private readonly string example3RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - AutoUploadEnabled=""true"" - MaxSize=""1024 * 1024 * 100"" - Label=""Select or drag and drop files"" - UploadUrl=""@UploadUrl"" />"; +<BitFileUpload Label=""Select or drag and drop files"" + AutoUpload + MultiSelect + UploadUrl=""@UploadUrl"" + MaxSize=""1024 * 1024 * 1"" />"; + private readonly string example3CsharpCode = @" +private string UploadUrl = $""/Upload"";"; private readonly string example4RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - AutoUploadEnabled=""false"" - AllowedExtensions=""@(new List<string> { "".gif"","".jpg"","".mp4"" })"" - Label=""Select or drag and drop files"" - UploadUrl=""@UploadUrl"" />"; +<BitFileUpload Label=""Select or drag and drop files"" + MultiSelect + UploadUrl=""@UploadUrl"" + AllowedExtensions=""@(new List<string> { "".gif"","".jpg"","".mp4"" })"" />"; + private readonly string example4CsharpCode = @" +private string UploadUrl = $""/Upload"";"; private readonly string example5RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - Label=""Select or drag and drop files"" +<BitFileUpload Label=""Select or drag and drop files"" + MultiSelect + ShowRemoveButton UploadUrl=""@UploadUrl"" - RemoveUrl=""@RemoveUrl"" - ShowRemoveButton=""true"" />"; + RemoveUrl=""@RemoveUrl"" />"; + private readonly string example5CsharpCode = @" +private string UploadUrl = $""/Upload""; +private string RemoveUrl = $""/Remove"";"; private readonly string example6RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - AutoUploadEnabled=""true"" - MaxSize=""1024 * 1024 * 500"" - UploadUrl=""@ChunkedUploadUrl"" - Label=""Select or drag and drop files"" +<BitFileUpload Label=""Select or drag and drop files"" + AutoUpload + MultiSelect + UploadUrl=""@UploadUrl"" OnAllUploadsComplete=""@(() => onAllUploadsCompleteText = ""All File Uploaded"")"" - OnUploading=""@(info => info.HttpHeaders = new Dictionary<string, string> { {""key1"", ""value1""} })"" />"; + OnUploading=""@(info => info.HttpHeaders = new Dictionary<string, string> { {""key1"", ""value1""} })"" /> + +<div>@onAllUploadsCompleteText</div>"; + private readonly string example6CsharpCode = @" +private string UploadUrl = $""/Upload""; +private string onAllUploadsCompleteText = ""No File"";"; private readonly string example7RazorCode = @" -<BitFileUpload IsMultiSelect=""true"" - Label=""Select or drag and drop files"" +<BitFileUpload Label=""Select or drag and drop files"" + MultiSelect UploadUrl=""@UploadUrl"" - UploadRequestHttpHeaders=""@(new Dictionary<string, string>{ {""header1"", ""value1"" } })"" - UploadRequestQueryStrings=""@(new Dictionary<string, string>{ {""qs1"", ""qsValue1"" } })"" RemoveUrl=""@RemoveUrl"" - RemoveRequestHttpHeaders=""@(new Dictionary<string, string>{ {""header2"", ""value2"" } })"" - RemoveRequestQueryStrings=""@(new Dictionary<string, string>{ {""qs2"", ""qsValue2"" } })"" />"; + UploadRequestQueryStrings=""@(new Dictionary<string, string>{ {""qs1"", ""qsValue1"" } })"" + UploadRequestHttpHeaders=""@(new Dictionary<string, string>{ {""header1"", ""value1"" } })"" + RemoveRequestQueryStrings=""@(new Dictionary<string, string>{ {""qs2"", ""qsValue2"" } })"" + RemoveRequestHttpHeaders=""@(new Dictionary<string, string>{ {""header2"", ""value2"" } })"" />"; + private readonly string example7CsharpCode = @" +private string UploadUrl = $""/Upload""; +private string RemoveUrl = $""/Remove"";"; private readonly string example8RazorCode = @" <BitFileUpload Label=""Select or drag and drop files"" - ChunkedUploadEnabled=""false"" - UploadUrl=""@UploadUrl"" />"; + ChunkedUpload + UploadUrl=""@ChunkedUploadUrl"" />"; + private readonly string example8CsharpCode = @" +private string ChunkedUploadUrl = $""/ChunkedUpload"";"; private readonly string example9RazorCode = @" <style> @@ -644,12 +674,12 @@ private async Task HandleBrowseFileOnClick() <BitFileUpload @ref=""bitFileUpload"" Label="""" - UploadUrl=""@NonChunkedUploadUrl"" + UploadUrl=""@UploadUrl"" RemoveUrl=""@RemoveUrl"" MaxSize=""1024 * 1024 * 2"" - AllowedExtensions=""@(new List<string> { "".jpeg"", "".jpg"", "".png"", "".bpm"" })"" SuccessfulUploadMessage=""File upload succeeded"" - NotAllowedExtensionErrorMessage=""File type not supported""> + NotAllowedExtensionErrorMessage=""File type not supported"" + AllowedExtensions=""@(new List<string> { "".jpeg"", "".jpg"", "".png"", "".bpm"" })""> <LabelTemplate> @if (FileUploadIsEmpty()) { @@ -725,8 +755,8 @@ Browse file private readonly string example9CsharpCode = @" [Inject] public IJSRuntime JSRuntime { get; set; } = default!; -private string NonChunkedUploadUrl => ""FileUpload/UploadNonChunkedFile""; -private string RemoveUrl => $""FileUpload/RemoveFile""; +private string UploadUrl => ""/Upload""; +private string RemoveUrl => $""/Remove""; private BitFileUpload bitFileUpload; private bool FileUploadIsEmpty() => !bitFileUpload.Files?.Any(f => f.Status != BitFileUploadStatus.Removed) ?? true; @@ -796,14 +826,13 @@ private bool IsFileTypeNotAllowed(BitFileInfo file) private readonly string example10RazorCode = @" <BitFileUpload @ref=""bitFileUploadWithBrowseFile"" Label="""" - UploadUrl=""@NonChunkedUploadUrl"" - RemoveUrl=""@RemoveUrl"" - MaxSize=""1024 * 1024 * 500"" /> + UploadUrl=""@UploadUrl"" + RemoveUrl=""@RemoveUrl"" /> <BitButton OnClick=""HandleBrowseFileOnClick"">Browse file</BitButton>"; private readonly string example10CsharpCode = @" -private string NonChunkedUploadUrl = ""/Upload""; -private string RemoveUrl => ""/RemoveFile""; +private string UploadUrl = ""/Upload""; +private string RemoveUrl = ""/Remove""; private BitFileUpload bitFileUploadWithBrowseFile; private async Task HandleBrowseFileOnClick() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/NumberField/BitNumberFieldDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/NumberField/BitNumberFieldDemo.razor.cs index 886aa2856c..7e6c464d42 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/NumberField/BitNumberFieldDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/NumberField/BitNumberFieldDemo.razor.cs @@ -58,8 +58,8 @@ public partial class BitNumberFieldDemo new() { Name = "DecrementIconName", - Type = "string", - DefaultValue = "ChevronDownSmall", + Type = "string?", + DefaultValue = "null", Description = "Custom icon name for the decrement button.", }, new() @@ -93,8 +93,8 @@ public partial class BitNumberFieldDemo new() { Name = "IncrementIconName", - Type = "string", - DefaultValue = "ChevronUpSmall", + Type = "string?", + DefaultValue = "null", Description = "Custom icon name for the increment button.", }, new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor index 4f43bc858a..4a07b5a7c0 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor @@ -10,6 +10,7 @@ ComponentDescription="A search box (SearchBox) provides an input field for searching content within a site or app to find specific items." ComponentParameters="componentParameters" ComponentSubClasses="componentSubClasses" + ComponentSubEnums="componentSubEnums" ComponentPublicMembers="componentPublicMembers"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.cs index 774aefa76b..51913864c2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.cs @@ -18,9 +18,9 @@ public partial class BitSearchBoxDemo Name = "Classes", Type = "BitSearchBoxClassStyles?", DefaultValue = "null", + Description = "Custom CSS classes for different parts of the BitSearchBox.", LinkType = LinkType.Link, Href = "#searchbox-class-styles", - Description = "Custom CSS classes for different parts of the BitSearchBox.", }, new() { @@ -65,6 +65,15 @@ public partial class BitSearchBoxDemo Description = "The icon name for the icon shown at the beginning of the search box.", }, new() + { + Name = "InputMode", + Type = "BitInputMode?", + DefaultValue = "null", + Description = "Sets the inputmode html attribute of the input element.", + LinkType = LinkType.Link, + Href = "#input-mode", + }, + new() { Name = "MaxSuggestCount", Type = "int", @@ -162,6 +171,7 @@ public partial class BitSearchBoxDemo Description = "Whether or not the SearchBox is underlined.", }, ]; + private readonly List<ComponentSubClass> componentSubClasses = [ new() @@ -169,8 +179,8 @@ public partial class BitSearchBoxDemo Id = "searchbox-class-styles", Title = "BitSearchBoxClassStyles", Description = "", - Parameters = new() - { + Parameters = + [ new() { Name = "Root", @@ -241,9 +251,71 @@ public partial class BitSearchBoxDemo DefaultValue = "null", Description = "Custom CSS classes/styles for the search box's search icon container.", } - } + ] } ]; + + private readonly List<ComponentSubEnum> componentSubEnums = + [ + new() + { + Id = "input-mode", + Name = "BitInputMode", + Description = "This allows a browser to display an appropriate virtual keyboard.", + Items = + [ + new() + { + Name= "None", + Description="The input expects text characters.", + Value="0", + }, + new() + { + Name= "Text", + Description="Standard input keyboard for the user's current locale.", + Value="1", + }, + new() + { + Name= "Decimal", + Description="Fractional numeric input keyboard containing the digits and decimal separator for the user's locale.", + Value="2", + }, + new() + { + Name= "Numeric", + Description="Numeric input keyboard, but only requires the digits 0–9.", + Value="3", + }, + new() + { + Name= "Tel", + Description="A telephone keypad input, including the digits 0–9, the asterisk (*), and the pound (#) key", + Value="4", + }, + new() + { + Name= "Search", + Description="A virtual keyboard optimized for search input.", + Value="5", + }, + new() + { + Name= "Email", + Description="A virtual keyboard optimized for entering email addresses.", + Value="6", + }, + new() + { + Name= "Url", + Description="A keypad optimized for entering URLs.", + Value="7", + } + ] + } + ]; + private readonly List<ComponentParameter> componentPublicMembers = [ new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SpinButton/BitSpinButtonDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SpinButton/BitSpinButtonDemo.razor.cs index 07b73c4ddb..fa52bfdf23 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SpinButton/BitSpinButtonDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SpinButton/BitSpinButtonDemo.razor.cs @@ -58,8 +58,8 @@ public partial class BitSpinButtonDemo new() { Name = "DecrementIconName", - Type = "string", - DefaultValue = "ChevronDownSmall", + Type = "string?", + DefaultValue = "null", Description = "Custom icon name for the decrement button.", }, new() @@ -107,8 +107,8 @@ public partial class BitSpinButtonDemo new() { Name = "IncrementTitle", - Type = "string", - DefaultValue = "ChevronUpSmall", + Type = "string?", + DefaultValue = "null", Description = "The title to show when the mouse is placed on the increment button.", }, new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor index 1284fc3389..364f2fc0b1 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor @@ -40,13 +40,13 @@ <div>Showcases the BitTextField with an underlined style, including variations with placeholders, disabled state, and required fields.</div> <br /> <div class="example-box"> - <BitTextField Label="Basic" IsUnderlined /> + <BitTextField Label="Basic" Underlined /> <br /> - <BitTextField Label="Placeholder" IsUnderlined Placeholder="Enter a text..." /> + <BitTextField Label="Placeholder" Underlined Placeholder="Enter a text..." /> <br /> - <BitTextField Label="Disabled" IsUnderlined IsEnabled="false" /> + <BitTextField Label="Disabled" Underlined IsEnabled="false" /> <br /> - <BitTextField Label="Required" IsUnderlined Required /> + <BitTextField Label="Required" Underlined Required /> </div> </ExamplePreview> </ComponentExampleBox> @@ -56,11 +56,11 @@ <div>Demonstrates BitTextField without borders, including variations for disabled and required fields.</div> <br /> <div class="example-box"> - <BitTextField Label="Basic" Placeholder="Enter a text..." HasBorder="false" /> + <BitTextField Label="Basic" Placeholder="Enter a text..." NoBorder /> <br /> - <BitTextField Label="Disabled" Placeholder="Enter a text..." HasBorder="false" IsEnabled="false" /> + <BitTextField Label="Disabled" Placeholder="Enter a text..." NoBorder IsEnabled="false" /> <br /> - <BitTextField Label="Required" Placeholder="Enter a text..." HasBorder="false" Required /> + <BitTextField Label="Required" Placeholder="Enter a text..." NoBorder Required /> </div> </ExamplePreview> </ComponentExampleBox> @@ -70,11 +70,11 @@ <div>Displays BitTextField in multiline mode with options for resizable and fixed sizes, as well as setting the number of rows.</div> <br /> <div class="example-box"> - <BitTextField Label="Resizable" IsMultiline /> + <BitTextField Label="Multiline" Multiline /> <br /> - <BitTextField Label="Unresizable (Fixed)" IsMultiline IsResizable="false" /> + <BitTextField Label="Resizable" Multiline Resizable /> <br /> - <BitTextField Label="Rows = 10" IsMultiline Rows="10" /> + <BitTextField Label="Rows = 10" Multiline Rows="10" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -184,7 +184,7 @@ <div>Demonstrates various binding scenarios with BitTextField, including one-way, two-way, on-change events, and immediate, debounce, and throttle options.</div> <br /> <div class="example-box"> - <BitTextField Label="Trimmed" IsTrimmed @bind-Value="trimmedValue" /> + <BitTextField Label="Trimmed" Trim @bind-Value="trimmedValue" /> <br /> <pre>[@trimmedValue]</pre> <br /><br /><br /> @@ -276,9 +276,9 @@ IconName="@BitIconName.EditMail" /> <br /> <br /> - <BitTextField IsUnderlined - Dir="BitDir.Rtl" + <BitTextField Underlined Label="تقویم" + Dir="BitDir.Rtl" IconName="@BitIconName.Calendar" /> </div> </div> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs index d2be74266e..6d50c9af39 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs @@ -12,13 +12,6 @@ public partial class BitTextFieldDemo Description = "AutoComplete is a string that maps to the autocomplete attribute of the HTML input element.", }, new() - { - Name = "AutoFocus", - Type = "bool", - DefaultValue = "false", - Description = "Determines if the text field is auto focused on first render.", - }, - new() { Name = "CanRevealPassword", Type = "bool", @@ -30,9 +23,9 @@ public partial class BitTextFieldDemo Name = "Classes", Type = "BitTextFieldClassStyles?", DefaultValue = "null", + Description = "Custom CSS classes for different parts of the BitTextField.", LinkType = LinkType.Link, Href = "#textfield-class-styles", - Description = "Custom CSS classes for different parts of the BitTextField.", }, new() { @@ -56,34 +49,6 @@ public partial class BitTextFieldDemo Description = "Shows the custom description for text field.", }, new() - { - Name = "HasBorder", - Type = "bool", - DefaultValue = "true", - Description = "Whether or not the text field is borderless.", - }, - new() - { - Name = "IsMultiline", - Type = "bool", - DefaultValue = "false", - Description = "Whether or not the text field is a Multiline text field.", - }, - new() - { - Name = "IsUnderlined", - Type = "bool", - DefaultValue = "false", - Description = "Whether or not the text field is underlined.", - }, - new() - { - Name = "IsResizable", - Type = "bool", - DefaultValue = "true", - Description = "For multiline text fields, whether or not the field is resizable.", - }, - new() { Name = "IconName", Type = "string?", @@ -92,10 +57,12 @@ public partial class BitTextFieldDemo }, new() { - Name = "IsTrimmed", - Type = "bool", - DefaultValue = "false", - Description = "Specifies whether to remove any leading or trailing whitespace from the value.", + Name = "InputMode", + Type = "BitInputMode?", + DefaultValue = "null", + Description = "Sets the inputmode html attribute of the input element.", + LinkType = LinkType.Link, + Href = "#input-mode", }, new() { @@ -119,6 +86,26 @@ public partial class BitTextFieldDemo Description = "Specifies the maximum number of characters allowed in the input.", }, new() + { + Name = "Multiline", + Type = "bool", + DefaultValue = "false", + Description = "Whether or not the text field is a Multiline text field.", + }, + new() + { + Name = "NoBorder", + Type = "bool", + DefaultValue = "false", + Description = "Whether or not the text field is borderless.", + }, + new() + { + Name = "OnClick", + Type = "EventCallback<MouseEventArgs>", + Description = "Callback for when the input clicked.", + }, + new() { Name = "OnFocus", Type = "EventCallback<FocusEventArgs>", @@ -149,12 +136,6 @@ public partial class BitTextFieldDemo Description = "Callback for When a keyboard key is released.", }, new() - { - Name = "OnClick", - Type = "EventCallback<MouseEventArgs>", - Description = "Callback for when the input clicked.", - }, - new() { Name = "Placeholder", Type = "string?", @@ -177,10 +158,10 @@ public partial class BitTextFieldDemo }, new() { - Name = "Rows", - Type = "int", - DefaultValue = "3", - Description = "For multiline text, Number of rows.", + Name = "Resizable", + Type = "bool", + DefaultValue = "false", + Description = "For multiline text fields, whether or not the field is resizable.", }, new() { @@ -190,6 +171,13 @@ public partial class BitTextFieldDemo Description = "Suffix displayed after the text field contents. This is not included in the value. \r\n Ensure a descriptive label is present to assist screen readers, as the value does not include the suffix.", }, new() + { + Name = "Rows", + Type = "int?", + DefaultValue = "null", + Description = "For multiline text, Number of rows.", + }, + new() { Name = "Styles", Type = "BitTextFieldClassStyles?", @@ -213,6 +201,13 @@ public partial class BitTextFieldDemo Description = "Shows the custom suffix for text field.", }, new() + { + Name = "Trim", + Type = "bool", + DefaultValue = "false", + Description = "Specifies whether to remove any leading or trailing whitespace from the value.", + }, + new() { Name = "Type", Type = "BitInputType", @@ -220,7 +215,14 @@ public partial class BitTextFieldDemo Description = "Input type.", LinkType = LinkType.Link, Href = "#input-type-enum" - } + }, + new() + { + Name = "Underlined", + Type = "bool", + DefaultValue = "false", + Description = "Whether or not the text field is underlined.", + }, ]; private readonly List<ComponentSubClass> componentSubClasses = @@ -394,6 +396,63 @@ public partial class BitTextFieldDemo Value="5", } ] + }, + new() + { + Id = "input-mode", + Name = "BitInputMode", + Description = "This allows a browser to display an appropriate virtual keyboard.", + Items = + [ + new() + { + Name= "None", + Description="The input expects text characters.", + Value="0", + }, + new() + { + Name= "Text", + Description="Standard input keyboard for the user's current locale.", + Value="1", + }, + new() + { + Name= "Decimal", + Description="Fractional numeric input keyboard containing the digits and decimal separator for the user's locale.", + Value="2", + }, + new() + { + Name= "Numeric", + Description="Numeric input keyboard, but only requires the digits 0–9.", + Value="3", + }, + new() + { + Name= "Tel", + Description="A telephone keypad input, including the digits 0–9, the asterisk (*), and the pound (#) key", + Value="4", + }, + new() + { + Name= "Search", + Description="A virtual keyboard optimized for search input.", + Value="5", + }, + new() + { + Name= "Email", + Description="A virtual keyboard optimized for entering email addresses.", + Value="6", + }, + new() + { + Name= "Url", + Description="A keypad optimized for entering URLs.", + Value="7", + } + ] } ]; @@ -461,20 +520,20 @@ private void HandleInvalidSubmit() <BitTextField Label=""Auto focused"" AutoFocus />"; private readonly string example2RazorCode = @" -<BitTextField Label=""Basic"" IsUnderlined /> -<BitTextField Label=""Placeholder"" IsUnderlined Placeholder=""Enter a text..."" /> -<BitTextField Label=""Disabled"" IsUnderlined IsEnabled=""false"" /> -<BitTextField Label=""Required"" IsUnderlined Required />"; +<BitTextField Label=""Basic"" Underlined /> +<BitTextField Label=""Placeholder"" Underlined Placeholder=""Enter a text..."" /> +<BitTextField Label=""Disabled"" Underlined IsEnabled=""false"" /> +<BitTextField Label=""Required"" Underlined Required />"; private readonly string example3RazorCode = @" -<BitTextField Label=""Basic"" Placeholder=""Enter a text..."" HasBorder=""false"" /> -<BitTextField Label=""Disabled"" Placeholder=""Enter a text..."" HasBorder=""false"" IsEnabled=""false"" /> -<BitTextField Label=""Required"" Placeholder=""Enter a text..."" HasBorder=""false"" Required />"; +<BitTextField Label=""Basic"" Placeholder=""Enter a text..."" NoBorder /> +<BitTextField Label=""Disabled"" Placeholder=""Enter a text..."" NoBorder IsEnabled=""false"" /> +<BitTextField Label=""Required"" Placeholder=""Enter a text..."" NoBorder Required />"; private readonly string example4RazorCode = @" -<BitTextField Label=""Resizable"" IsMultiline /> -<BitTextField Label=""Unresizable (Fixed)"" IsMultiline IsResizable=""false"" /> -<BitTextField Label=""Rows = 10"" IsMultiline Rows=""10"" />"; +<BitTextField Label=""Multiline"" Multiline /> +<BitTextField Label=""Resizable"" Multiline Resizable /> +<BitTextField Label=""Rows = 10"" Multiline Rows=""10"" />"; private readonly string example5RazorCode = @" <BitTextField Label=""Email"" IconName=""@BitIconName.EditMail"" /> @@ -544,7 +603,7 @@ private void HandleInvalidSubmit() private string? throttleValue;"; private readonly string example10RazorCode = @" -<BitTextField Label=""Trimmed"" IsTrimmed @bind-Value=""trimmedValue"" /> +<BitTextField Label=""Trimmed"" Trim @bind-Value=""trimmedValue"" /> <pre>[@trimmedValue]</pre> <BitTextField Label=""Not Trimmed"" @bind-Value=""notTrimmedValue"" /> @@ -722,8 +781,8 @@ private void HandleValidSubmit() { } Placeholder=""پست الکترونیکی"" IconName=""@BitIconName.EditMail"" /> -<BitTextField IsUnderlined - Dir=""BitDir.Rtl"" +<BitTextField Underlined Label=""تقویم"" + Dir=""BitDir.Rtl"" IconName=""@BitIconName.Calendar"" />"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor index 5bc3bc495e..45669800af 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor @@ -10,66 +10,71 @@ ComponentSubClasses="componentSubClasses"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitToggle Label="Basic" /> - <br /> - <BitToggle Label="Disabled" IsEnabled="false" /> - </div> + <BitToggle Label="Basic" /> + <br /> + <BitToggle Label="Disabled" IsEnabled="false" /> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Texts" RazorCode="@example2RazorCode" Id="example2"> <ExamplePreview> - <div class="example-box"> - <div>Customize BitToggle On and Off texts, providing more context for its state.</div> - <br /> - <BitToggle Label="DefaultText" DefaultText="This is a good toggle!" /> - <br /><br /> - <BitToggle Label="OnText & OffText" OnText="Toggle is On" OffText="Toggle is Off" /> - </div> + <div>Customize BitToggle On and Off texts, providing more context for its state.</div><br /> + <BitToggle Label="Text" Text="This is a toggle!" /> + <br /><br /> + <BitToggle Label="OnText & OffText" OnText="Toggle is On" OffText="Toggle is Off" /> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Label" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> - <div class="example-box"> - <div>Use inline labels and custom label templates in BitToggle.</div> - <br /><br /> - <div>IsInlineLabel:</div> - <br /> - <BitToggle Label="This is an inline label" IsInlineLabel /> - <br /><br /><br /><br /> - <div>LabelTemplate:</div> - <br /> - <BitToggle> - <LabelTemplate> - <div style="display:flex;align-items:center;gap:10px"> - <BitLabel Style="color:green">This is custom Label</BitLabel> - <BitIcon IconName="@BitIconName.Filter" /> - </div> - </LabelTemplate> - </BitToggle> - </div> + <div>Use inline labels and custom label templates in BitToggle.</div> + <br /><br /> + <div>Inline:</div><br /> + <BitToggle Label="This is an inline label" Inline /> + <br /><br /><br /><br /> + <div>LabelTemplate:</div><br /> + <BitToggle> + <LabelTemplate> + <div style="display:flex;align-items:center;gap:10px"> + <BitLabel Style="color:green">This is custom Label</BitLabel> + <BitIcon IconName="@BitIconName.Filter" /> + </div> + </LabelTemplate> + </BitToggle> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Reversed" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> - <div class="example-box"> - <div>Reverse the label and input position of BitToggle.</div> - <br /><br /> - <div>Default:</div> - <br /> - <BitToggle Reversed Label="This is a reversed label" /> - <br /><br /> - <div>IsInlineLabel:</div> - <br /> - <BitToggle Reversed IsInlineLabel Label="This is a reversed inline label" /> - </div> + <div>Reverse the label and input position of BitToggle.</div> + <br /><br /> + <div>Default:</div><br /> + <BitToggle Label="This is a reversed label" Reversed /> + <br /><br /><br /><br /> + <div>Inline:</div><br /> + <BitToggle Label="This is a reversed inline label" Reversed Inline /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Binding" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> + <ComponentExampleBox Title="FullWidth" RazorCode="@example5RazorCode" Id="example5"> + <ExamplePreview> + <div>Full width BitToggle in Inline mode.</div> + <br /><br /> + <div>Default:</div><br /> + <BitToggle Label="This is a full-width toggle" FullWidth Inline /> + <br /><br /><br /><br /> + <div>Reversed:</div><br /> + <BitToggle Label="This is a reversed full-width toggle" Reversed FullWidth Inline /> + <br /><br /> + <BitToggle FullWidth Inline> + <LabelTemplate> + <BitActionButton FullWidth>go go go</BitActionButton> + </LabelTemplate> + </BitToggle> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Binding" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> <div class="example-box"> <div>Bind value one-way and two-way in BitToggle</div> @@ -85,7 +90,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example6RazorCode" Id="example6"> + <ComponentExampleBox Title="Style & Class" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> <div class="example-box"> <div>Customize the appearance of BitToggle using styles and CSS classes.</div> @@ -103,7 +108,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Validation" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> + <ComponentExampleBox Title="Validation" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> <div> <div>Use BitToggle within a form and validate its state.</div> @@ -113,7 +118,7 @@ <EditForm Model="validationModel" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInvalidSubmit"> <DataAnnotationsValidator /> - <BitToggle Label="Terms and conditions" DefaultText="I agree." @bind-Value="validationModel.TermsAgreement" /> + <BitToggle Label="Terms and conditions" Text="I agree." @bind-Value="validationModel.TermsAgreement" /> <ValidationMessage For="@(() => validationModel.TermsAgreement)" /> <br /> @@ -129,11 +134,17 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" Id="example8"> + <ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> <div>Use BitToggle in right-to-left (RTL).</div> <br /> - <BitToggle Dir="BitDir.Rtl" OnText="روشن" OffText="خاموش" /> + <div dir="rtl"> + <BitToggle Label="این یک تاگل است" Dir="BitDir.Rtl" OnText="روشن" OffText="خاموش" /> + <br /><br /><br /> + <BitToggle Label="این یک تاگل خطی است" Dir="BitDir.Rtl" Inline /> + <br /><br /><br /> + <BitToggle Label="این یک تاگل خطی برعکس است" Dir="BitDir.Rtl" Reversed Inline /> + </div> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor.cs index 0fcb92a40c..f381c6e3e2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Toggle/BitToggleDemo.razor.cs @@ -2,30 +2,37 @@ public partial class BitToggleDemo { - private readonly List<ComponentParameter> componentParameters = new() - { + private readonly List<ComponentParameter> componentParameters = + [ new() { Name = "Classes", Type = "BitToggleClassStyles?", DefaultValue = "null", + Description = "Custom CSS classes for different parts of the toggle.", LinkType = LinkType.Link, - Href = "#toggle-class-styles", - Description = "Custom CSS classes for different parts of the BitToggle.", + Href = "#class-styles", }, new() { - Name = "DefaultText", - Type = "string?", + Name = "DefaultValue", + Type = "bool?", DefaultValue = "null", - Description = "Default text used when the On or Off texts are null.", + Description = "The default value of the toggle when the value parameter has not been assigned.", + }, + new() + { + Name = "FullWidth", + Type = "bool", + DefaultValue = "false", + Description = "Renders the toggle in full width of its container while putting space between the label and the knob.", }, new() { - Name = "IsInlineLabel", + Name = "Inline", Type = "bool", DefaultValue = "false", - Description = "Whether the label (not the onText/offText) should be positioned inline with the toggle control. Left (right in RTL) side when on/off text provided VS right (left in RTL) side when there is no on/off text.", + Description = "Renders the label and the knob in a single line together.", }, new() { @@ -67,20 +74,27 @@ public partial class BitToggleDemo Name = "Styles", Type = "BitToggleClassStyles?", DefaultValue = "null", + Description = "Custom CSS styles for different parts of the toggle.", LinkType = LinkType.Link, - Href = "#toggle-class-styles", - Description = "Custom CSS styles for different parts of the BitToggle.", - } - }; + Href = "#class-styles", + }, + new() + { + Name = "Text", + Type = "string?", + DefaultValue = "null", + Description = "Default text used when the On or Off texts are null.", + }, + ]; - private readonly List<ComponentSubClass> componentSubClasses = new() - { + private readonly List<ComponentSubClass> componentSubClasses = + [ new() { - Id = "toggle-class-styles", + Id = "class-styles", Title = "BitToggleClassStyles", - Parameters = new() - { + Parameters = + [ new() { Name = "Root", @@ -130,9 +144,9 @@ public partial class BitToggleDemo DefaultValue = "null", Description = "Custom CSS classes/styles for the text of the BitToggle." } - } + ] } - }; + ]; @@ -162,11 +176,11 @@ private void HandleInvalidSubmit() <BitToggle Label=""Disabled"" IsEnabled=""false"" />"; private readonly string example2RazorCode = @" -<BitToggle Label=""DefaultText"" DefaultText=""This is a good toggle!"" /> +<BitToggle Label=""Text"" Text=""This is a toggle!"" /> <BitToggle Label=""OnText & OffText"" OnText=""Toggle is On"" OffText=""Toggle is Off"" />"; private readonly string example3RazorCode = @" -<BitToggle Label=""This is an inline label"" IsInlineLabel /> +<BitToggle Label=""This is an inline label"" Inline /> <BitToggle> <LabelTemplate> @@ -178,21 +192,26 @@ private void HandleInvalidSubmit() </BitToggle>"; private readonly string example4RazorCode = @" -<BitToggle Reversed Label=""This is a reversed label"" /> +<BitToggle Label=""This is a reversed label"" Reversed /> -<BitToggle Reversed IsInlineLabel Label=""This is a reversed inline label"" />"; +<BitToggle Label=""This is a reversed inline label"" Reversed Inline />"; private readonly string example5RazorCode = @" +<BitToggle Label=""This is a full-width toggle"" FullWidth Inline /> + +<BitToggle Label=""This is a reversed full-width toggle"" Reversed FullWidth Inline />"; + + private readonly string example6RazorCode = @" <BitToggle Label=""One-way"" Value=""oneWayValue"" /> <BitToggleButton @bind-IsChecked=""oneWayValue"" OnText=""On"" OffText=""Off"" /> <BitToggle Label=""Two-way"" @bind-Value=""twoWayValue"" /> <BitToggleButton @bind-IsChecked=""twoWayValue"" OnText=""On"" OffText=""Off"" />"; - private readonly string example5CsharpCode = @" + private readonly string example6CsharpCode = @" private bool oneWayValue; private bool twoWayValue;"; - private readonly string example6RazorCode = @" + private readonly string example7RazorCode = @" <style> .custom-thumb { background: #fff; @@ -233,7 +252,7 @@ private void HandleInvalidSubmit() Button = ""custom-button"", Checked = ""custom-check"" } )"" />"; - private readonly string example7RazorCode = @" + private readonly string example8RazorCode = @" <style> .validation-message { color: red; @@ -243,12 +262,12 @@ private void HandleInvalidSubmit() <EditForm Model=""validationModel"" OnValidSubmit=""HandleValidSubmit"" OnInvalidSubmit=""HandleInvalidSubmit""> <DataAnnotationsValidator /> - <BitToggle Label=""Terms and conditions"" DefaultText=""I agree."" @bind-Value=""validationModel.TermsAgreement"" /> + <BitToggle Label=""Terms and conditions"" Text=""I agree."" @bind-Value=""validationModel.TermsAgreement"" /> <ValidationMessage For=""@(() => validationModel.TermsAgreement)"" /> <BitButton ButtonType=""BitButtonType.Submit"">Submit</BitButton> </EditForm>"; - private readonly string example7CsharpCode = @" + private readonly string example8CsharpCode = @" public class BitToggleValidationModel { [Range(typeof(bool), ""true"", ""true"", ErrorMessage = ""You must agree to the terms and conditions."")] @@ -260,6 +279,10 @@ public class BitToggleValidationModel private async Task HandleValidSubmit() { } private void HandleInvalidSubmit() { }"; - private readonly string example8RazorCode = @" -<BitToggle Dir=""BitDir.Rtl"" OnText=""روشن"" OffText=""خاموش"" />"; + private readonly string example9RazorCode = @" +<BitToggle Label=""این یک تاگل است"" Dir=""BitDir.Rtl"" OnText=""روشن"" OffText=""خاموش"" /> + +<BitToggle Label=""این یک تاگل خطی است"" Dir=""BitDir.Rtl"" Inline /> + +<BitToggle Label=""این یک تاگل خطی برعکس است"" Dir=""BitDir.Rtl"" Reversed Inline />"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Layouts/Stack/BitStackDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Layouts/Stack/BitStackDemo.razor.cs index a525ddaee8..9b0dcbeecc 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Layouts/Stack/BitStackDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Layouts/Stack/BitStackDemo.razor.cs @@ -3,6 +3,15 @@ public partial class BitStackDemo { private readonly List<ComponentParameter> componentParameters = [ + new() + { + Name = "Alignment", + Type = "BitAlignment?", + DefaultValue = "null", + Description = "Defines whether to render Stack children both horizontally and vertically.", + LinkType = LinkType.Link, + Href = "#alignment-enum", + }, new() { Name = "AutoHeight", @@ -39,6 +48,34 @@ public partial class BitStackDemo Description = "The custom html element used for the root node. The default is \"div\"." }, new() + { + Name = "FillContent", + Type = "bool", + DefaultValue = "false", + Description = "Expand the direct children to occupy all of the root element's width." + }, + new() + { + Name = "FitHeight", + Type = "bool", + DefaultValue = "false", + Description = "Sets the height of the stack to fit its content." + }, + new() + { + Name = "FitSize", + Type = "bool", + DefaultValue = "false", + Description = "Sets the width and height of the stack to fit its content." + }, + new() + { + Name = "FitWidth", + Type = "bool", + DefaultValue = "false", + Description = "Sets the width of the stack to fit its content." + }, + new() { Name = "Gap", Type = "string?", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/TimelineActionItem.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/Event.cs similarity index 59% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/TimelineActionItem.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/Event.cs index 4d507630fd..7f3fd337be 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/TimelineActionItem.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/Event.cs @@ -1,12 +1,12 @@ namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Lists.Timeline; -public class TimelineActionItem +public class Event { public string? Class { get; set; } - public RenderFragment<TimelineActionItem>? DotContent { get; set; } + public RenderFragment<Event>? DotContent { get; set; } - public RenderFragment<TimelineActionItem>? FirstContent { get; set; } + public RenderFragment<Event>? FirstContent { get; set; } public string? FirstText { get; set; } @@ -16,7 +16,7 @@ public class TimelineActionItem public bool Reversed { get; set; } - public RenderFragment<TimelineActionItem>? SecondContent { get; set; } + public RenderFragment<Event>? SecondContent { get; set; } public string? SecondText { get; set; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor index 0db3370515..3f96f874a9 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor @@ -214,7 +214,7 @@ @code { - private List<TimelineActionItem> templateItems = new() + private List<Event> templateItems = new() { new() { @@ -226,9 +226,9 @@ DotContent = (item => @<div class="dot-template"><BitRingLoading CustomSize="30" Color="BitColor.Tertiary" /></div>), SecondContent = (item => @<div style="display: flex; align-items: center; gap: 1rem;"> - <BitIcon IconName="Accept" Style="color: limegreen;" /> - <BitLabel>Software Engineer</BitLabel> - </div>) + <BitIcon IconName="Accept" Style="color: limegreen;" /> + <BitLabel>Software Engineer</BitLabel> + </div>) }, new() { @@ -239,9 +239,9 @@ DotContent = (item => @<div class="dot-template"><BitSpinnerLoading CustomSize="30" Color="BitColor.Tertiary" /></div>), SecondContent = (item => @<div style="display: flex; align-items: center; gap: 1rem;"> - <BitIcon IconName="Accept" Style="color: limegreen;" /> - <BitLabel>Co-Founder & CTO</BitLabel> - </div>), + <BitIcon IconName="Accept" Style="color: limegreen;" /> + <BitLabel>Co-Founder & CTO</BitLabel> + </div>), Reversed = true }, new() @@ -254,9 +254,9 @@ DotContent = (item => @<div class="dot-template"><BitRollerLoading CustomSize="30" Color="BitColor.Tertiary" /></div>), SecondContent = (item => @<div style="display: flex; align-items: center; gap: 1rem;"> - <BitIcon IconName="Accept" Style="color: limegreen;" /> - <BitLabel>Project Manager</BitLabel> - </div>) + <BitIcon IconName="Accept" Style="color: limegreen;" /> + <BitLabel>Project Manager</BitLabel> + </div>) }, }; } \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.cs index 000ed9d2f3..2900c8cb14 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.cs @@ -1,8 +1,10 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Lists.Timeline; +using Microsoft.Extensions.Logging; + +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Lists.Timeline; public partial class _BitTimelineCustomDemo { - BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() + BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, @@ -13,41 +15,41 @@ public partial class _BitTimelineCustomDemo SecondaryContent = { Selector = i => i.SecondContent }, }; - private List<TimelineActionItem> basicCustoms = + private List<Event> basicCustoms = [ new() { FirstText = "Custom 1" }, new() { FirstText = "Custom 2", SecondText = "Custom 2 Secondary" }, new() { FirstText = "Custom 3" } ]; - private List<TimelineActionItem> disabledCustoms = + private List<Event> disabledCustoms = [ new() { FirstText = "Custom 1" }, new() { FirstText = "Custom 2", SecondText = "Custom 2 Secondary", Disabled = true }, new() { FirstText = "Custom 3" } ]; - private List<TimelineActionItem> iconCustoms = + private List<Event> iconCustoms = [ new() { FirstText = "Custom 1", Icon = BitIconName.Add }, new() { FirstText = "Custom 2", Icon = BitIconName.Edit, SecondText = "Custom 2 Secondary", Disabled = true }, new() { FirstText = "Custom 3", Icon = BitIconName.Delete } ]; - private List<TimelineActionItem> reversedCustoms = + private List<Event> reversedCustoms = [ new() { FirstText = "Custom 1" }, new() { FirstText = "Custom 2", Reversed = true }, new() { FirstText = "Custom 3" } ]; - private List<TimelineActionItem> styleClassCustoms = + private List<Event> styleClassCustoms = [ new() { FirstText = "Styled", Style = "color: dodgerblue;", Icon = BitIconName.Brush }, new() { FirstText = "Classed", Class = "custom-item", Icon = BitIconName.FormatPainter } ]; - private List<TimelineActionItem> basicRtlCustoms = + private List<Event> basicRtlCustoms = [ new() { FirstText = "گزینه ۱" }, new() { FirstText = "گزینه ۲", SecondText = "گزینه ۲ ثانویه" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs index c9b46b25da..eb7c94639c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs @@ -5,19 +5,19 @@ public partial class _BitTimelineCustomDemo private readonly string example1RazorCode = @" <BitTimeline Items=""basicCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example1CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText } }; -private List<TimelineActionItem> basicCustoms = +private List<Event> basicCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"" }, @@ -27,19 +27,19 @@ public class TimelineActionItem private readonly string example2RazorCode = @" <BitTimeline Horizontal Items=""basicCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example2CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText } }; -private List<TimelineActionItem> basicCustoms = +private List<Event> basicCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"" }, @@ -50,28 +50,28 @@ public class TimelineActionItem <BitTimeline Horizontal Items=""basicCustoms"" NameSelectors=""nameSelectors"" IsEnabled=""false"" /> <BitTimeline Horizontal Items=""disabledCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example3CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } public bool Disabled { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, IsEnabled = { Selector = i => i.Disabled is false }, }; -private List<TimelineActionItem> basicCustoms = +private List<Event> basicCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"" }, new() { FirstText = ""Custom 3"" } ]; -private List<TimelineActionItem> disabledCustoms = +private List<Event> disabledCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"", Disabled = true }, @@ -83,21 +83,21 @@ public class TimelineActionItem <BitTimeline Horizontal Variant=""BitVariant.Outline"" Items=""disabledCustoms"" NameSelectors=""nameSelectors"" /> <BitTimeline Horizontal Variant=""BitVariant.Text"" Items=""disabledCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example4CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } public bool Disabled { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, IsEnabled = { Selector = i => i.Disabled is false }, }; -private List<TimelineActionItem> disabledCustoms = +private List<Event> disabledCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"", Disabled = true }, @@ -109,7 +109,7 @@ public class TimelineActionItem <BitTimeline Horizontal Items=""iconCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Outline"" /> <BitTimeline Horizontal Items=""iconCustoms"" NameSelectors=""nameSelectors"" Variant=""BitVariant.Text"" />"; private readonly string example5CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } @@ -117,7 +117,7 @@ public class TimelineActionItem public string? Icon { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, @@ -125,7 +125,7 @@ public class TimelineActionItem IconName = { Selector = i => i.Icon }, }; -private List<TimelineActionItem> iconCustoms = +private List<Event> iconCustoms = [ new() { FirstText = ""Custom 1"", Icon = BitIconName.Add }, new() { FirstText = ""Custom 2"", Icon = BitIconName.Edit, SecondText = ""Item 2 Secondary"", Disabled = true }, @@ -139,27 +139,27 @@ public class TimelineActionItem <BitTimeline Horizontal Items=""basicCustoms"" NameSelectors=""nameSelectors"" Reversed /> <BitTimeline Horizontal Items=""reversedCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example6CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } public bool Reversed { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText } }; -private List<TimelineActionItem> basicCustoms = +private List<Event> basicCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"" }, new() { FirstText = ""Custom 3"" } ]; -private List<TimelineActionItem> reversedCustoms = +private List<Event> reversedCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", Reversed = true }, @@ -180,22 +180,22 @@ public class TimelineActionItem <BitTimeline Horizontal Items=""templateItems"" NameSelectors=""nameSelectors"" />"; private readonly string example7CsharpCode = @" -public class TimelineActionItem +public class Event { - public RenderFragment<TimelineActionItem>? FirstContent { get; set; } - public RenderFragment<TimelineActionItem>? DotContent { get; set; } - public RenderFragment<TimelineActionItem>? SecondContent { get; set; } + public RenderFragment<Event>? FirstContent { get; set; } + public RenderFragment<Event>? DotContent { get; set; } + public RenderFragment<Event>? SecondContent { get; set; } public bool Reversed { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryContent = { Selector = i => i.FirstContent }, DotTemplate = { Selector = i => i.DotContent }, SecondaryContent = { Selector = i => i.SecondContent }, }; -private List<TimelineActionItem> templateItems = +private List<Event> templateItems = [ new() { @@ -274,7 +274,7 @@ public class TimelineActionItem <BitTimeline Horizontal Color=""BitColor.Error"" Variant=""BitVariant.Outline"" Items=""iconCustoms"" NameSelectors=""nameSelectors"" /> <BitTimeline Horizontal Color=""BitColor.Error"" Variant=""BitVariant.Text"" Items=""iconCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example8CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } @@ -282,7 +282,7 @@ public class TimelineActionItem public string? Icon { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, @@ -290,7 +290,7 @@ public class TimelineActionItem IconName = { Selector = i => i.Icon }, }; -private List<TimelineActionItem> iconCustoms = +private List<Event> iconCustoms = [ new() { FirstText = ""Custom 1"", Icon = BitIconName.Add }, new() { FirstText = ""Custom 2"", Icon = BitIconName.Edit, SecondText = ""Item 2 Secondary"", Disabled = true }, @@ -310,7 +310,7 @@ public class TimelineActionItem <BitTimeline Horizontal Size=""BitSize.Large"" Variant=""BitVariant.Outline"" Items=""iconCustoms"" NameSelectors=""nameSelectors"" /> <BitTimeline Horizontal Size=""BitSize.Large"" Variant=""BitVariant.Text"" Items=""iconCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example9CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } @@ -318,7 +318,7 @@ public class TimelineActionItem public string? Icon { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, @@ -326,7 +326,7 @@ public class TimelineActionItem IconName = { Selector = i => i.Icon }, }; -private List<TimelineActionItem> iconCustoms = +private List<Event> iconCustoms = [ new() { FirstText = ""Custom 1"", Icon = BitIconName.Add }, new() { FirstText = ""Custom 2"", Icon = BitIconName.Edit, SecondText = ""Item 2 Secondary"", Disabled = true }, @@ -390,7 +390,7 @@ public class TimelineActionItem Item = ""custom-item-text"", Divider = ""custom-divider"" })"" />"; private readonly string example10CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } @@ -400,7 +400,7 @@ public class TimelineActionItem public string? Style { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText }, @@ -408,20 +408,20 @@ public class TimelineActionItem IconName = { Selector = i => i.Icon }, }; -private List<TimelineActionItem> basicCustoms = +private List<Event> basicCustoms = [ new() { FirstText = ""Custom 1"" }, new() { FirstText = ""Custom 2"", SecondText = ""Custom 2 Secondary"" }, new() { FirstText = ""Custom 3"" } ]; -private List<TimelineActionItem> styleClassCustoms = +private List<Event> styleClassCustoms = [ new() { FirstText = ""Styled"", Style = ""color: dodgerblue;"", Icon = BitIconName.Brush }, new() { FirstText = ""Classed"", Class = ""custom-item"", Icon = BitIconName.FormatPainter } ]; -private List<TimelineActionItem> iconCustoms = new() +private List<Event> iconCustoms = new() { new() { FirstText = ""Custom 1"", Icon = BitIconName.Add }, new() { FirstText = ""Custom 2"", Icon = BitIconName.Edit }, @@ -432,19 +432,19 @@ public class TimelineActionItem <BitTimeline Dir=""BitDir.Rtl"" Items=""basicRtlCustoms"" NameSelectors=""nameSelectors"" /> <BitTimeline Horizontal Dir=""BitDir.Rtl"" Items=""basicRtlCustoms"" NameSelectors=""nameSelectors"" />"; private readonly string example11CsharpCode = @" -public class TimelineActionItem +public class Event { public string? FirstText { get; set; } public string? SecondText { get; set; } } -BitTimelineNameSelectors<TimelineActionItem> nameSelectors = new() +BitTimelineNameSelectors<Event> nameSelectors = new() { PrimaryText = { Selector = i => i.FirstText }, SecondaryText = { Selector = i => i.SecondText } }; -private List<TimelineActionItem> basicRtlCustoms = +private List<Event> basicRtlCustoms = [ new() { FirstText = ""گزینه ۱"" }, new() { FirstText = ""گزینه ۲"", SecondText = ""گزینه ۲ ثانویه"" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.cs index 677eb4ac88..ff8a5b2a96 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.cs @@ -12,6 +12,15 @@ public partial class BitBreadcrumbDemo Description = "The content of the BitBreadcrumb, that are BitBreadOption components." }, new() + { + Name = "Classes", + Type = "BitBreadcrumbClassStyles?", + DefaultValue = "null", + Description = "Custom CSS classes for different parts of the breadcrumb.", + LinkType = LinkType.Link, + Href = "#class-styles", + }, + new() { Name = "DividerIconName", Type = "string?", @@ -19,11 +28,25 @@ public partial class BitBreadcrumbDemo Description = "The divider icon name." }, new() + { + Name = "DividerIconTemplate", + Type = "RenderFragment?", + DefaultValue = "null", + Description = "The custom template content to render divider icon." + }, + new() { Name = "Items", Type = "IList<TItem>", - DefaultValue = "new List<TItem>()", - Description = "Collection of breadcrumbs to render" + DefaultValue = "[]", + Description = "Collection of BreadLists to render." + }, + new() + { + Name = "ItemTemplate", + Type = "RenderFragment<TItem>?", + DefaultValue = "null", + Description = "The custom template content to render each item." }, new() { @@ -42,6 +65,19 @@ public partial class BitBreadcrumbDemo Href = "#name-selectors" }, new() + { + Name = "OnItemClick", + Type = "EventCallback<TItem>", + Description = "Callback for when the breadcrumb item clicked." + }, + new() + { + Name = "Options", + Type = "RenderFragment?", + DefaultValue = "null", + Description = "Alias of the ChildContent." + }, + new() { Name = "OverflowAriaLabel", Type = "string?", @@ -64,23 +100,33 @@ public partial class BitBreadcrumbDemo }, new() { - Name = "OnItemClick", - Type = "EventCallback<TItem>", - Description = "Callback for when the breadcrumb item clicked." + Name = "OverflowIconTemplate", + Type = "RenderFragment?", + DefaultValue= "null", + Description = "The custom template content to render each overflow icon." }, new() { - Name = "SelectedItemClass", - Type = "string?", - DefaultValue = "null", - Description = "The CSS class attribute for the selected item." + Name = "OverflowTemplate", + Type = "RenderFragment<TItem>?", + DefaultValue= "null", + Description = "The custom template content to render each item in overflow list." }, new() { - Name = "SelectedItemStyle", - Type = "string?", + Name = "ReversedIcon", + Type = "bool", + DefaultValue = "false", + Description = "Reverses the positions of the icon and the item text of the item content." + }, + new() + { + Name = "Styles", + Type = "BitBreadcrumbClassStyles?", DefaultValue = "null", - Description = "The style attribute for selected item." + Description = "Custom CSS styles for different parts of the breadcrumb.", + LinkType = LinkType.Link, + Href = "#class-styles", } ]; @@ -123,6 +169,18 @@ public partial class BitBreadcrumbDemo Description = "Style attribute for breadcrumb item.", }, new() + { + Name = "IconName", + Type = "string?", + Description = "Name of an icon to render next to the item text.", + }, + new() + { + Name = "ReversedIcon", + Type = "bool?", + Description = "Reverses the positions of the icon and the item text of the item content.", + }, + new() { Name = "IsSelected", Type = "bool", @@ -140,6 +198,18 @@ public partial class BitBreadcrumbDemo Name = "OnClick", Type = "Action<BitBreadcrumbItem>?", Description = "Click event handler of the breadcrumb item.", + }, + new() + { + Name = "OverflowTemplate", + Type = "RenderFragment<BitBreadcrumbItem>?", + Description = "The custom template for the item in overflow list.", + }, + new() + { + Name = "Template", + Type = "RenderFragment<BitBreadcrumbItem>?", + Description = "The custom template for the item.", } ] }, @@ -180,6 +250,18 @@ public partial class BitBreadcrumbDemo Description = "Style attribute for breadcrumb option.", }, new() + { + Name = "IconName", + Type = "string?", + Description = "Name of an icon to render next to the item text.", + }, + new() + { + Name = "ReversedIcon", + Type = "bool?", + Description = "Reverses the positions of the icon and the item text of the item content.", + }, + new() { Name = "IsSelected", Type = "bool", @@ -197,10 +279,163 @@ public partial class BitBreadcrumbDemo Name = "OnClick", Type = "EventCallback<BitBreadcrumbOption>", Description = "Click event handler of the breadcrumb option.", + }, + new() + { + Name = "OverflowTemplate", + Type = "RenderFragment<BitBreadcrumbItem>?", + Description = "The custom template for the item in overflow list.", + }, + new() + { + Name = "Template", + Type = "RenderFragment<BitBreadcrumbItem>?", + Description = "The custom template for the item.", } ] }, new() + { + Id = "class-styles", + Title = "BitBreadcrumbClassStyles", + Parameters = + [ + new() + { + Name = "Root", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the root element of the BitBreadcrumb.", + }, + new() + { + Name = "Overlay", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the overlay of the BitBreadcrumb.", + }, + new() + { + Name = "ItemContainer", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the item container of the BitBreadcrumb." + }, + new() + { + Name = "OverflowButton", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the overflow button of the BitBreadcrumb." + }, + new() + { + Name = "OverflowButtonIcon", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the overflow button icon of the BitBreadcrumb." + }, + new() + { + Name = "ItemWrapper", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the item wrapper of the BitBreadcrumb." + }, + new() + { + Name = "Item", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each item of the BitBreadcrumb." + }, + new() + { + Name = "ItemIcon", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each item icon of the BitBreadcrumb." + }, + new() + { + Name = "ItemText", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each item text of the BitBreadcrumb." + }, + new() + { + Name = "SelectedItem", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the selected item of the BitBreadcrumb." + }, + new() + { + Name = "Divider", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the divider of the BitBreadcrumb." + }, + new() + { + Name = "DividerIcon", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the divider icon of the BitBreadcrumb." + }, + new() + { + Name = "Callout", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the callout element of the BitBreadcrumb." + }, + new() + { + Name = "CalloutContainer", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the callout container of the BitBreadcrumb." + }, + new() + { + Name = "OverflowItemWrapper", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the overflow item wrapper of the BitBreadcrumb." + }, + new() + { + Name = "OverflowItem", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each overflow item of the BitBreadcrumb." + }, + new() + { + Name = "OverflowItemIcon", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each overflow item icon of the BitBreadcrumb." + }, + new() + { + Name = "OverflowItemText", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for each overflow item text of the BitBreadcrumb." + }, + new() + { + Name = "OverflowSelectedItem", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the overflow selected item of the BitBreadcrumb." + } + ], + }, + new() { Id = "name-selectors", Title = "BitBreadcrumbNameSelectors<TItem>", @@ -252,6 +487,24 @@ public partial class BitBreadcrumbDemo Href = "#name-selector-pair" }, new() + { + Name = "IconName", + Type = "BitNameSelectorPair<TItem, string?>", + DefaultValue = "new(nameof(BitBreadcrumbItem.IconName))", + Description = "The IconName field name and selector of the custom input class.", + LinkType = LinkType.Link, + Href = "#name-selector-pair" + }, + new() + { + Name = "ReversedIcon", + Type = "BitNameSelectorPair<TItem, bool?>", + DefaultValue = "new(nameof(BitBreadcrumbItem.ReversedIcon))", + Description = "The ReversedIcon field name and selector of the custom input class.", + LinkType = LinkType.Link, + Href = "#name-selector-pair" + }, + new() { Name = "IsSelected", Type = "BitNameSelectorPair<TItem, bool>", @@ -274,6 +527,24 @@ public partial class BitBreadcrumbDemo Name = "OnClick", Type = "Action<TItem>?", Description = "Click event handler of the item.", + }, + new() + { + Name = "OverflowTemplate", + Type = "BitNameSelectorPair<TItem, RenderFragment<TItem>?>", + DefaultValue = "new(nameof(BitBreadcrumbItem.OverflowTemplate))", + Description = "The OverflowTemplate field name and selector of the custom input class.", + LinkType = LinkType.Link, + Href = "#name-selector-pair" + }, + new() + { + Name = "Template", + Type = "BitNameSelectorPair<TItem, RenderFragment<TItem>?>", + DefaultValue = "new(nameof(BitBreadcrumbItem.Template))", + Description = "The Template field name and selector of the custom input class.", + LinkType = LinkType.Link, + Href = "#name-selector-pair" } ], }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.scss index 7c06119f29..a17723210f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/BitBreadcrumbDemo.razor.scss @@ -12,6 +12,12 @@ } } + .custom-class { + font-style: italic; + text-shadow: dodgerblue 0 0 0.5rem; + border-bottom: 1px solid dodgerblue; + } + .custom-item { color: #ffcece; @@ -21,6 +27,24 @@ } } + .custom-item-1 { + color: #b6ff00; + + &:hover { + color: #2aff00; + background: transparent; + } + } + + .custom-item-2 { + color: #ffd800; + + &:hover { + color: #ff6a00; + background: transparent; + } + } + .custom-selected-item { color: blueviolet; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfo.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfo.cs new file mode 100644 index 0000000000..7c9be354d3 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfo.cs @@ -0,0 +1,22 @@ +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Navs.Breadcrumb; + +public class PageInfo +{ + public string? Name { get; set; } + + public string? Address { get; set; } + + public string? HtmlClass { get; set; } + + public string? HtmlStyle { get; set; } + + public string? Icon { get; set; } + + public bool IsCurrent { get; set; } + + public bool IsEnabled { get; set; } = true; + + public RenderFragment<PageInfo>? Fragment { get; set; } + + public RenderFragment<PageInfo>? OverflowFragment { get; set; } +} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfoModel.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfoModel.cs deleted file mode 100644 index 9c9b895d1a..0000000000 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/PageInfoModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Navs.Breadcrumb; - -public class PageInfoModel -{ - public string Name { get; set; } = default!; - - public string Address { get; set; } = default!; - - public string HtmlClass { get; set; } = default!; - - public string HtmlStyle { get; set; } = default!; - - public bool IsCurrent { get; set; } - - public bool IsEnabled { get; set; } = true; -} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor index de2a5b5239..240552c4a0 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor @@ -72,59 +72,74 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="OverflowIcon" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> +<ComponentExampleBox Title="Icons" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div>Customize the overflow icon in BitBreadcrumb using different icon name values.</div> + <div>Customize the overflow icon, divider icon and item icons in BitBreadcrumb using different icon name values.</div> <br /><br /> <div class="example-box"> <div> - <div>BitIconName (ChevronDown)</div> - <BitBreadcrumb Items="CustomBreadcrumbItems" + <div>BitIconName (OverflowIcon: ChevronDown - DividerIcon: CaretRightSolid8)</div> + <BitBreadcrumb Items="CustomBreadcrumbItemsWithIcon" NameSelectors="nameSelectors" MaxDisplayedItems="3" OverflowIndex="2" + DividerIconName="@BitIconName.CaretRightSolid8" OverflowIconName="@BitIconName.ChevronDown" /> </div> <br /> <div> - <div>BitIconName (CollapseMenu)</div> - <BitBreadcrumb Items="CustomBreadcrumbItems" + <div>BitIconName (OverflowIcon: CollapseMenu) with active ReversedIcon</div> + <BitBreadcrumb Items="CustomBreadcrumbItemsWithIcon" NameSelectors="nameSelectors" MaxDisplayedItems="3" OverflowIndex="2" - OverflowIconName="@BitIconName.CollapseMenu" /> + OverflowIconName="@BitIconName.CollapseMenu" + ReversedIcon /> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Class & Style" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> +<ComponentExampleBox Title="Templates" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> - <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <div>Here are some examples of customizing the breadcrumb content.</div> <br /><br /> <div class="example-box"> <div> - <div>Items Class</div> - <BitBreadcrumb Items="CustomBreadcrumbItemsWithClass" - NameSelectors="nameSelectors" /> - </div> - <div> - <div>Items Style</div> - <BitBreadcrumb Items="CustomBreadcrumbItemsWithStyle" - NameSelectors="nameSelectors" /> + <div>DividerIconTemplate</div> + <BitBreadcrumb Items="CustomBreadcrumbItems" + NameSelectors="nameSelectors"> + <DividerIconTemplate> + <BitIcon IconName="@BitIconName.CaretRightSolid8" Color="BitColor.Warning" /> + </DividerIconTemplate> + </BitBreadcrumb> </div> <br /> <div> - <div>Selected Item Class</div> + <div>ItemTemplate & OverflowTemplate</div> <BitBreadcrumb Items="CustomBreadcrumbItems" NameSelectors="nameSelectors" - SelectedItemClass="custom-selected-item" /> + MaxDisplayedItems="3" + OverflowIndex="2"> + <ItemTemplate Context="item"> + <div style="font-weight: bold; color: #d13438; font-style:italic;"> + @item.Name + </div> + </ItemTemplate> + <OverflowTemplate Context="item"> + <div style="font-weight: bold; color: blueviolet; font-style:italic;"> + @item.Name + </div> + </OverflowTemplate> + </BitBreadcrumb> </div> + <br /> <div> - <div>Selected Item Style</div> - <BitBreadcrumb Items="CustomBreadcrumbItems" + <div>Item's template & Item's overflow tTemplate</div> + <BitBreadcrumb Items="CustomBreadcrumbItemTemplateItems" NameSelectors="nameSelectors" - SelectedItemStyle="color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;" /> + MaxDisplayedItems="3" + OverflowIndex="2" /> </div> </div> </ExamplePreview> @@ -139,8 +154,8 @@ NameSelectors="nameSelectors" MaxDisplayedItems="3" OverflowIndex="2" - OnItemClick="(PageInfoModel model) => HandleOnCustomClick(model)" - SelectedItemStyle="color: dodgerblue;" /> + OnItemClick="(PageInfo model) => HandleOnCustomClick(model)" + Styles="@(new() { SelectedItem = "color: dodgerblue;", OverflowSelectedItem = "color: red;" })" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -155,7 +170,7 @@ NameSelectors="nameSelectors" MaxDisplayedItems="@MaxDisplayedItems" OverflowIndex="@OverflowIndex" - OnItemClick="(PageInfoModel model) => HandleOnCustomizedCustomClick(model)" /> + OnItemClick="(PageInfo model) => HandleOnCustomizedCustomClick(model)" /> </div> <div class="operators"> <div> @@ -172,7 +187,46 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> +<ComponentExampleBox Title="Class & Style" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> + <ExamplePreview> + <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <br /><br /> + <div class="example-box"> + <div> + <div>Component's Style & Class:</div> + <BitBreadcrumb Items="CustomBreadcrumbItems" + NameSelectors="nameSelectors" + Class="custom-class" /> + <br /> + <BitBreadcrumb Items="CustomBreadcrumbItems" + NameSelectors="nameSelectors" + Style="font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;" /> + </div> + <br /> + <div> + <div>Item's Style & Class:</div> + <BitBreadcrumb Items="CustomBreadcrumbItemsWithClass" + NameSelectors="nameSelectors" /> + <br /> + <BitBreadcrumb Items="CustomBreadcrumbItemsWithStyle" + NameSelectors="nameSelectors" /> + </div> + <br /> + <div> + <div>Styles & Classes:</div> + <BitBreadcrumb Items="CustomBreadcrumbItems" + NameSelectors="nameSelectors" + Classes="@(new() { Item = "custom-item", SelectedItem = "custom-selected-item" })" /> + <br /> + <BitBreadcrumb Items="CustomBreadcrumbItems" + NameSelectors="nameSelectors" + Styles="@(new() { Item = "color: green;", SelectedItem = "color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;" })" /> + </div> + </div> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> <div>Use BitBreadcrumb in right-to-left (RTL).</div> <br /> @@ -184,4 +238,34 @@ NameSelectors="nameSelectors" /> </div> </ExamplePreview> -</ComponentExampleBox> \ No newline at end of file +</ComponentExampleBox> + +@code { + private readonly List<PageInfo> CustomBreadcrumbItemTemplateItems = + [ + new() + { + Name = "Item 1", Address = "/components/breadcrumb", + Fragment = (item => @<div style="color:green">@item.Name</div>), + OverflowFragment = (item => @<div style="color:green;text-decoration:underline;">@item.Name</div>) + }, + new () + { + Name = "Item 2", Address = "/components/breadcrumb", + Fragment = (item => @<div style="color:yellow">@item.Name</div>), + OverflowFragment = (item => @<div style="color:yellow;text-decoration:underline;">@item.Name</div>) + }, + new() + { + Name = "Item 3", Address = "/components/breadcrumb", + Fragment = (item => @<div style="color:red">@item.Name</div>), + OverflowFragment = (item => @<div style="color:red;text-decoration:underline;">@item.Name</div>) + }, + new() + { + Name = "Item 4", Address = "/components/breadcrumb", IsCurrent = true, + Fragment = (item => @<div style="color:blue">@item.Name</div>), + OverflowFragment = (item => @<div style="color:blue;text-decoration:underline;">@item.Name</div>) + } + ]; +} \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.cs index f349a0dc89..850b35253e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.cs @@ -6,7 +6,7 @@ public partial class _BitBreadcrumbCustomDemo private uint OverflowIndex = 2; private uint MaxDisplayedItems = 3; - private readonly List<PageInfoModel> CustomBreadcrumbItems = + private readonly List<PageInfo> CustomBreadcrumbItems = [ new() { Name = "Custom 1", Address = "/components/breadcrumb" }, new() { Name = "Custom 2", Address = "/components/breadcrumb" }, @@ -14,7 +14,7 @@ public partial class _BitBreadcrumbCustomDemo new() { Name = "Custom 4", Address = "/components/breadcrumb", IsCurrent = true } ]; - private readonly List<PageInfoModel> CustomBreadcrumbItemsDisabled = + private readonly List<PageInfo> CustomBreadcrumbItemsDisabled = [ new() { Name = "Custom 1", Address = "/components/breadcrumb", IsEnabled = false }, new() { Name = "Custom 2", Address = "/components/breadcrumb", IsEnabled = false }, @@ -22,23 +22,31 @@ public partial class _BitBreadcrumbCustomDemo new() { Name = "Custom 4", Address = "/components/breadcrumb", IsCurrent = true } ]; - private readonly List<PageInfoModel> CustomBreadcrumbItemsWithClass = + private readonly List<PageInfo> CustomBreadcrumbItemsWithIcon = [ - new() { Name = "Custom 1", Address = "/components/breadcrumb", HtmlClass = "custom-item" }, - new() { Name = "Custom 2", Address = "/components/breadcrumb", HtmlClass = "custom-item" }, - new() { Name = "Custom 3", Address = "/components/breadcrumb", HtmlClass = "custom-item" }, - new() { Name = "Custom 4", Address = "/components/breadcrumb", HtmlClass = "custom-item", IsCurrent = true } + new() { Name = "Custom 1", Address = "/components/breadcrumb", Icon = BitIconName.AdminELogoInverse32 }, + new() { Name = "Custom 2", Address = "/components/breadcrumb", Icon = BitIconName.AppsContent }, + new() { Name = "Custom 3", Address = "/components/breadcrumb", Icon = BitIconName.AzureIcon }, + new() { Name = "Custom 4", Address = "/components/breadcrumb", Icon = BitIconName.ClassNotebookLogo16, IsCurrent = true } ]; - private readonly List<PageInfoModel> CustomBreadcrumbItemsWithStyle = + private readonly List<PageInfo> CustomBreadcrumbItemsWithClass = + [ + new() { Name = "Custom 1", Address = "/components/breadcrumb", HtmlClass = "custom-item-1" }, + new() { Name = "Custom 2", Address = "/components/breadcrumb", HtmlClass = "custom-item-2" }, + new() { Name = "Custom 3", Address = "/components/breadcrumb", HtmlClass = "custom-item-1" }, + new() { Name = "Custom 4", Address = "/components/breadcrumb", HtmlClass = "custom-item-2", IsCurrent = true } + ]; + + private readonly List<PageInfo> CustomBreadcrumbItemsWithStyle = [ new() { Name = "Custom 1", Address = "/components/breadcrumb", HtmlStyle = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, - new() { Name = "Custom 2", Address = "/components/breadcrumb", HtmlStyle = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, + new() { Name = "Custom 2", Address = "/components/breadcrumb", HtmlStyle = "color: aqua; text-shadow: aqua 0 0 1rem;" }, new() { Name = "Custom 3", Address = "/components/breadcrumb", HtmlStyle = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, - new() { Name = "Custom 4", Address = "/components/breadcrumb", HtmlStyle = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;", IsCurrent = true } + new() { Name = "Custom 4", Address = "/components/breadcrumb", HtmlStyle = "color: aqua; text-shadow: aqua 0 0 1rem;", IsCurrent = true } ]; - private readonly List<PageInfoModel> CustomBreadcrumbItemsWithControlled = + private readonly List<PageInfo> CustomBreadcrumbItemsWithControlled = [ new() { Name = "Custom 1" }, new() { Name = "Custom 2" }, @@ -48,7 +56,7 @@ public partial class _BitBreadcrumbCustomDemo new() { Name = "Custom 6", IsCurrent = true } ]; - private readonly List<PageInfoModel> CustomBreadcrumbItemsWithCustomized = + private readonly List<PageInfo> CustomBreadcrumbItemsWithCustomized = [ new() { Name = "Custom 1" }, new() { Name = "Custom 2" }, @@ -56,7 +64,7 @@ public partial class _BitBreadcrumbCustomDemo new() { Name = "Custom 4", IsCurrent = true } ]; - private readonly List<PageInfoModel> RtlCustomBreadcrumbItems = + private readonly List<PageInfo> RtlCustomBreadcrumbItems = [ new() { Name = "پوشه اول" }, new() { Name = "پوشه دوم", IsCurrent = true }, @@ -66,22 +74,25 @@ public partial class _BitBreadcrumbCustomDemo new() { Name = "پوشه ششم" }, ]; - private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() + private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, IsSelected = { Selector = c => c.IsCurrent }, Class = { Selector = c => c.HtmlClass }, - Style = { Selector = c => c.HtmlStyle } + Style = { Selector = c => c.HtmlStyle }, + IconName = { Selector = c => c.Icon }, + Template = { Name = nameof(PageInfo.Fragment) }, + OverflowTemplate = { Name = nameof(PageInfo.OverflowFragment) } }; - private void HandleOnCustomClick(PageInfoModel model) + private void HandleOnCustomClick(PageInfo model) { CustomBreadcrumbItemsWithControlled.First(i => i.IsCurrent).IsCurrent = false; model.IsCurrent = true; } - private void HandleOnCustomizedCustomClick(PageInfoModel model) + private void HandleOnCustomizedCustomClick(PageInfo model) { CustomBreadcrumbItemsWithCustomized.First(i => i.IsCurrent).IsCurrent = false; model.IsCurrent = true; @@ -90,7 +101,7 @@ private void HandleOnCustomizedCustomClick(PageInfoModel model) private void AddCustomItem() { ItemsCount++; - CustomBreadcrumbItemsWithCustomized.Add(new PageInfoModel() + CustomBreadcrumbItemsWithCustomized.Add(new PageInfo() { Name = $"Custom {ItemsCount}" }); diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.samples.cs index cfde02e845..de61847a32 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbCustomDemo.razor.samples.cs @@ -12,7 +12,7 @@ public partial class _BitBreadcrumbCustomDemo <BitBreadcrumb Items=""CustomBreadcrumbItemsDisabled"" NameSelectors=""nameSelectors"" />"; private readonly string example1CsharpCode = @" -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -27,7 +27,7 @@ public class PageInfoModel public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> CustomBreadcrumbItems = +private readonly List<PageInfo> CustomBreadcrumbItems = [ new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"" }, new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"" }, @@ -35,7 +35,7 @@ public class PageInfoModel new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } ]; -private readonly List<PageInfoModel> CustomBreadcrumbItemsDisabled = +private readonly List<PageInfo> CustomBreadcrumbItemsDisabled = [ new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", IsEnabled = false }, new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", IsEnabled = false }, @@ -43,7 +43,7 @@ public class PageInfoModel new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, @@ -80,7 +80,7 @@ public class PageInfoModel MaxDisplayedItems=""3"" OverflowIndex=""2"" />"; private readonly string example2CsharpCode = @" -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -95,7 +95,7 @@ public class PageInfoModel public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> CustomBreadcrumbItems = +private readonly List<PageInfo> CustomBreadcrumbItems = [ new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"" }, new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"" }, @@ -103,7 +103,7 @@ public class PageInfoModel new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, @@ -113,19 +113,21 @@ public class PageInfoModel };"; private readonly string example3RazorCode = @" -<BitBreadcrumb Items=""CustomBreadcrumbItems"" +<BitBreadcrumb Items=""CustomBreadcrumbItemsWithIcon"" NameSelectors=""nameSelectors"" MaxDisplayedItems=""3"" OverflowIndex=""2"" - OverflowIcon=""@BitIconName.ChevronDown"" /> + DividerIconName=""@BitIconName.CaretRightSolid8"" + OverflowIconName=""@BitIconName.ChevronDown"" /> -<BitBreadcrumb Items=""CustomBreadcrumbItems"" +<BitBreadcrumb Items=""CustomBreadcrumbItemsWithIcon"" NameSelectors=""nameSelectors"" MaxDisplayedItems=""3"" OverflowIndex=""2"" - OverflowIcon=""@BitIconName.CollapseMenu"" />"; + OverflowIconName=""@BitIconName.CollapseMenu"" + ReversedIcon />"; private readonly string example3CsharpCode = @" -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -135,68 +137,61 @@ public class PageInfoModel public string HtmlStyle { get; set; } + public string Icon { get; set; } + public bool IsCurrent { get; set; } public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> CustomBreadcrumbItems = +private readonly List<PageInfo> CustomBreadcrumbItemsWithIcon = [ - new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"" }, - new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"" }, - new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"" }, - new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } + new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", Icon = BitIconName.AdminELogoInverse32 }, + new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", Icon = BitIconName.AppsContent }, + new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"", Icon = BitIconName.AzureIcon }, + new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", Icon = BitIconName.ClassNotebookLogo16, IsCurrent = true } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, IsSelected = { Selector = c => c.IsCurrent }, Class = { Selector = c => c.HtmlClass }, - Style = { Selector = c => c.HtmlStyle } + Style = { Selector = c => c.HtmlStyle }, + IconName = { Selector = c => c.Icon }, };"; private readonly string example4RazorCode = @" -<style> - .custom-item { - color: #ffcece; - } - - .custom-item:hover { - color: #ff6868; - background: transparent; - } - - - .custom-selected-item { - color: blueviolet; - } - - .custom-selected-item:hover { - color: blueviolet; - background: transparent; - text-shadow: blueviolet 0 0 1rem; - } -</style> - - -<BitBreadcrumb Items=""CustomBreadcrumbItemsWithClass"" - NameSelectors=""nameSelectors"" /> - -<BitBreadcrumb Items=""CustomBreadcrumbItemsWithStyle"" - NameSelectors=""nameSelectors"" /> - - <BitBreadcrumb Items=""CustomBreadcrumbItems"" - NameSelectors=""nameSelectors"" - SelectedItemClass=""custom-selected-item"" /> + NameSelectors=""nameSelectors""> + <DividerIconTemplate> + <BitIcon IconName=""@BitIconName.CaretRightSolid8"" Color=""BitColor.Warning"" /> + </DividerIconTemplate> +</BitBreadcrumb> <BitBreadcrumb Items=""CustomBreadcrumbItems"" NameSelectors=""nameSelectors"" - SelectedItemStyle=""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"" />"; + MaxDisplayedItems=""3"" + OverflowIndex=""2""> + <ItemTemplate Context=""item""> + <div style=""font-weight: bold; color: #d13438; font-style:italic;""> + @item.Name + </div> + </ItemTemplate> + <OverflowTemplate Context=""item""> + <div style=""font-weight: bold; color: blueviolet; font-style:italic;""> + @item.Name + </div> + </OverflowTemplate> +</BitBreadcrumb> + +<BitBreadcrumb Items=""CustomBreadcrumbItemTemplateItems"" + NameSelectors=""nameSelectors"" + MaxDisplayedItems=""3"" + OverflowIndex=""2"" />"; private readonly string example4CsharpCode = @" -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -209,9 +204,13 @@ public class PageInfoModel public bool IsCurrent { get; set; } public bool IsEnabled { get; set; } = true; + + public RenderFragment<PageInfo>? Fragment { get; set; } + + public RenderFragment<PageInfo>? OverflowFragment { get; set; } } -private readonly List<PageInfoModel> CustomBreadcrumbItems = +private readonly List<PageInfo> CustomBreadcrumbItems = [ new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"" }, new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"" }, @@ -219,29 +218,43 @@ public class PageInfoModel new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } ]; -private readonly List<PageInfoModel> CustomBreadcrumbItemsWithClass = -[ - new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item"" }, - new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item"" }, - new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item"" }, - new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item"", IsCurrent = true } -]; - -private readonly List<PageInfoModel> CustomBreadcrumbItemsWithStyle = +private readonly List<PageInfo> CustomBreadcrumbItemTemplateItems = [ - new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"", IsCurrent = true } + new() + { + Name = ""Item 1"", Address = ""/components/breadcrumb"", + Fragment = (item => @<div style=""color:green"">@item.Name</div>), + OverflowFragment = (item => @<div style=""color:green;text-decoration:underline;"">@item.Name</div>) + }, + new () + { + Name = ""Item 2"", Address = ""/components/breadcrumb"", + Fragment = (item => @<div style=""color:yellow"">@item.Name</div>), + OverflowFragment = (item => @<div style=""color:yellow;text-decoration:underline;"">@item.Name</div>) + }, + new() + { + Name = ""Item 3"", Address = ""/components/breadcrumb"", + Fragment = (item => @<div style=""color:red"">@item.Name</div>), + OverflowFragment = (item => @<div style=""color:red;text-decoration:underline;"">@item.Name</div>) + }, + new() + { + Name = ""Item 4"", Address = ""/components/breadcrumb"", IsCurrent = true, + Fragment = (item => @<div style=""color:blue"">@item.Name</div>), + OverflowFragment = (item => @<div style=""color:blue;text-decoration:underline;"">@item.Name</div>) + } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, IsSelected = { Selector = c => c.IsCurrent }, Class = { Selector = c => c.HtmlClass }, - Style = { Selector = c => c.HtmlStyle } + Style = { Selector = c => c.HtmlStyle }, + Template = { Name = nameof(PageInfo.Fragment) }, + OverflowTemplate = { Name = nameof(PageInfo.OverflowFragment) } };"; private readonly string example5RazorCode = @" @@ -249,10 +262,10 @@ public class PageInfoModel NameSelectors=""nameSelectors"" MaxDisplayedItems=""3"" OverflowIndex=""2"" - OnItemClick=""(PageInfoModel model) => HandleOnCustomClick(model)"" - SelectedItemStyle=""color: dodgerblue;"" />"; + OnItemClick=""(PageInfo model) => HandleOnCustomClick(model)"" + Styles=""@(new() { SelectedItem = ""color: dodgerblue;"", OverflowSelectedItem = ""color: red;"" })"" />"; private readonly string example5CsharpCode = @" -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -267,7 +280,7 @@ public class PageInfoModel public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> CustomBreadcrumbItemsWithControlled = +private readonly List<PageInfo> CustomBreadcrumbItemsWithControlled = [ new() { Name = ""Custom 1"" }, new() { Name = ""Custom 2"" }, @@ -277,7 +290,7 @@ public class PageInfoModel new() { Name = ""Custom 6"", IsCurrent = true } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, @@ -286,7 +299,7 @@ public class PageInfoModel Style = { Selector = c => c.HtmlStyle } }; -private void HandleOnCustomClick(PageInfoModel model) +private void HandleOnCustomClick(PageInfo model) { CustomBreadcrumbItemsWithControlled.First(i => i.IsCurrent).IsCurrent = false; model.IsCurrent = true; @@ -297,7 +310,7 @@ private void HandleOnCustomClick(PageInfoModel model) NameSelectors=""nameSelectors"" MaxDisplayedItems=""@MaxDisplayedItems"" OverflowIndex=""@OverflowIndex"" - OnItemClick=""(PageInfoModel model) => HandleOnCustomizedCustomClick(model)"" /> + OnItemClick=""(PageInfo model) => HandleOnCustomizedCustomClick(model)"" /> <BitButton OnClick=""AddCustomItem"">Add Item</BitButton> <BitButton OnClick=""RemoveCustomItem"">Remove Item</BitButton> @@ -309,7 +322,7 @@ private void HandleOnCustomClick(PageInfoModel model) private uint OverflowIndex = 2; private uint MaxDisplayedItems = 3; -public class PageInfoModel +public class PageInfo { public string Name { get; set; } @@ -324,7 +337,7 @@ public class PageInfoModel public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> CustomBreadcrumbItemsWithCustomized = +private readonly List<PageInfo> CustomBreadcrumbItemsWithCustomized = [ new() { Name = ""Custom 1"" }, new() { Name = ""Custom 2"" }, @@ -332,7 +345,7 @@ public class PageInfoModel new() { Name = ""Custom 4"", IsCurrent = true } ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, @@ -341,7 +354,7 @@ public class PageInfoModel Style = { Selector = c => c.HtmlStyle } }; -private void HandleOnCustomizedCustomClick(PageInfoModel model) +private void HandleOnCustomizedCustomClick(PageInfo model) { CustomBreadcrumbItemsWithCustomized.First(i => i.IsCurrent).IsCurrent = false; model.IsCurrent = true; @@ -373,13 +386,129 @@ private void RemoveCustomItem() }"; private readonly string example7RazorCode = @" +<style> + .custom-class { + font-style: italic; + text-shadow: dodgerblue 0 0 0.5rem; + border-bottom: 1px solid dodgerblue; + } + + .custom-item { + color: #ffcece; + + &:hover { + color: #ff6868; + background: transparent; + } + } + + .custom-item-1 { + color: #b6ff00; + + &:hover { + color: #2aff00; + background: transparent; + } + } + + .custom-item-2 { + color: #ffd800; + + &:hover { + color: #ff6a00; + background: transparent; + } + } + + .custom-selected-item { + color: blueviolet; + + &:hover { + color: blueviolet; + background: transparent; + text-shadow: blueviolet 0 0 1rem; + } + } +</style> + +<BitBreadcrumb Items=""CustomBreadcrumbItems"" + NameSelectors=""nameSelectors"" + Class=""custom-class"" /> + +<BitBreadcrumb Items=""CustomBreadcrumbItems"" + NameSelectors=""nameSelectors"" + Style=""font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;"" /> + +<BitBreadcrumb Items=""CustomBreadcrumbItemsWithClass"" + NameSelectors=""nameSelectors"" /> + +<BitBreadcrumb Items=""CustomBreadcrumbItemsWithStyle"" + NameSelectors=""nameSelectors"" /> + +<BitBreadcrumb Items=""CustomBreadcrumbItems"" + NameSelectors=""nameSelectors"" + Classes=""@(new() { Item = ""custom-item"", SelectedItem = ""custom-selected-item"" })"" /> + +<BitBreadcrumb Items=""CustomBreadcrumbItems"" + NameSelectors=""nameSelectors"" + Styles=""@(new() { Item = ""color: green;"", SelectedItem = ""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"" })"" />"; + private readonly string example7CsharpCode = @" +public class PageInfo +{ + public string Name { get; set; } + + public string Address { get; set; } + + public string HtmlClass { get; set; } + + public string HtmlStyle { get; set; } + + public bool IsCurrent { get; set; } + + public bool IsEnabled { get; set; } = true; +} + +private readonly List<PageInfo> CustomBreadcrumbItems = +[ + new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"" }, + new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"" }, + new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"" }, + new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", IsCurrent = true } +]; + +private readonly List<PageInfo> CustomBreadcrumbItemsWithClass = +[ + new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item-1"" }, + new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item-2"" }, + new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item-1"" }, + new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", HtmlClass = ""custom-item-2"", IsCurrent = true } +]; + +private readonly List<PageInfo> CustomBreadcrumbItemsWithStyle = +[ + new() { Name = ""Custom 1"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, + new() { Name = ""Custom 2"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: aqua; text-shadow: aqua 0 0 1rem;"" }, + new() { Name = ""Custom 3"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, + new() { Name = ""Custom 4"", Address = ""/components/breadcrumb"", HtmlStyle = ""color: aqua; text-shadow: aqua 0 0 1rem;"", IsCurrent = true } +]; + +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() +{ + Text = { Selector = c => c.Name }, + Href = { Selector = c => c.Address }, + IsSelected = { Selector = c => c.IsCurrent }, + Class = { Selector = c => c.HtmlClass }, + Style = { Selector = c => c.HtmlStyle } +};"; + + private readonly string example8RazorCode = @" <BitBreadcrumb Dir=""BitDir.Rtl"" OverflowIndex=""2"" MaxDisplayedItems=""3"" Items=""RtlCustomBreadcrumbItems"" NameSelectors=""nameSelectors"" />"; - private readonly string example7CsharpCode = @" -public class PageInfoModel + private readonly string example8CsharpCode = @" +public class PageInfo { public string Name { get; set; } @@ -394,7 +523,7 @@ public class PageInfoModel public bool IsEnabled { get; set; } = true; } -private readonly List<PageInfoModel> RtlCustomBreadcrumbItems = +private readonly List<PageInfo> RtlCustomBreadcrumbItems = [ new() { Name = ""پوشه اول"" }, new() { Name = ""پوشه دوم"", IsCurrent = true }, @@ -404,7 +533,7 @@ public class PageInfoModel new() { Name = ""پوشه ششم"" }, ]; -private BitBreadcrumbNameSelectors<PageInfoModel> nameSelectors = new() +private BitBreadcrumbNameSelectors<PageInfo> nameSelectors = new() { Text = { Selector = c => c.Name }, Href = { Selector = c => c.Address }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor index 62ec04546a..3ebb1a506f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor @@ -53,53 +53,69 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="OverflowIcon" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> +<ComponentExampleBox Title="Icons" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div>Customize the overflow icon in BitBreadcrumb using different icon name values.</div> + <div>Customize the overflow icon, divider icon and item icons in BitBreadcrumb using different icon name values.</div> <br /><br /> <div class="example-box"> <div> - <div>BitIconName (ChevronDown)</div> - <BitBreadcrumb Items="BreadcrumbItems" - MaxDisplayedItems="3" + <div>BitIconName (OverflowIcon: ChevronDown - DividerIcon: CaretRightSolid8)</div> + <BitBreadcrumb Items="BreadcrumbItemsWitIcon" + DividerIconName="@BitIconName.CaretRightSolid8" + OverflowIconName="@BitIconName.ChevronDown" OverflowIndex="2" - OverflowIconName="@BitIconName.ChevronDown" /> + MaxDisplayedItems="3" /> </div> <br /> <div> - <div>BitIconName (CollapseMenu)</div> - <BitBreadcrumb Items="BreadcrumbItems" + <div>BitIconName (OverflowIcon: CollapseMenu) with active ReversedIcon</div> + <BitBreadcrumb Items="BreadcrumbItemsWitIcon" + OverflowIconName="@BitIconName.CollapseMenu" MaxDisplayedItems="3" OverflowIndex="2" - OverflowIconName="@BitIconName.CollapseMenu" /> + ReversedIcon /> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Class & Style" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> +<ComponentExampleBox Title="Templates" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> - <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <div>Here are some examples of customizing the breadcrumb content.</div> <br /><br /> <div class="example-box"> <div> - <div>Items Class</div> - <BitBreadcrumb Items="BreadcrumbItemsWithClass" /> - </div> - <div> - <div>Items Style</div> - <BitBreadcrumb Items="BreadcrumbItemsWithStyle" /> + <div>DividerIconTemplate</div> + <BitBreadcrumb Items="BreadcrumbItems"> + <DividerIconTemplate> + <BitIcon IconName="@BitIconName.CaretRightSolid8" Color="BitColor.Warning" /> + </DividerIconTemplate> + </BitBreadcrumb> </div> <br /> <div> - <div>Selected Item Class</div> + <div>ItemTemplate & OverflowTemplate</div> <BitBreadcrumb Items="BreadcrumbItems" - SelectedItemClass="custom-selected-item" /> + MaxDisplayedItems="3" + OverflowIndex="2"> + <ItemTemplate Context="item"> + <div style="font-weight: bold; color: #d13438; font-style:italic;"> + @item.Text + </div> + </ItemTemplate> + <OverflowTemplate Context="item"> + <div style="font-weight: bold; color: blueviolet; font-style:italic;"> + @item.Text + </div> + </OverflowTemplate> + </BitBreadcrumb> </div> + <br /> <div> - <div>Selected Item Style</div> - <BitBreadcrumb Items="BreadcrumbItems" - SelectedItemStyle="color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;" /> + <div>Item's template & Item's overflow tTemplate</div> + <BitBreadcrumb Items="BreadcrumbItemTemplateItems" + MaxDisplayedItems="3" + OverflowIndex="2" /> </div> </div> </ExamplePreview> @@ -114,7 +130,7 @@ MaxDisplayedItems="3" OverflowIndex="2" OnItemClick="(BitBreadcrumbItem item) => HandleOnItemClick(item)" - SelectedItemStyle="color: dodgerblue;" /> + Styles="@(new() { SelectedItem = "color: dodgerblue;", OverflowSelectedItem = "color: red;" })" /> </div> </ExamplePreview> </ComponentExampleBox> @@ -145,7 +161,38 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> +<ComponentExampleBox Title="Class & Style" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> + <ExamplePreview> + <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <br /><br /> + <div class="example-box"> + <div> + <div>Component's Style & Class:</div> + <BitBreadcrumb Items="BreadcrumbItems" Class="custom-class" /> + <br /> + <BitBreadcrumb Items="BreadcrumbItems" Style="font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;" /> + </div> + <br /> + <div> + <div>Item's Style & Class:</div> + <BitBreadcrumb Items="BreadcrumbItemsWithClass" /> + <br /> + <BitBreadcrumb Items="BreadcrumbItemsWithStyle" /> + </div> + <br /> + <div> + <div>Styles & Classes:</div> + <BitBreadcrumb Items="BreadcrumbItems" + Classes="@(new() { Item = "custom-item", SelectedItem = "custom-selected-item" })" /> + <br /> + <BitBreadcrumb Items="BreadcrumbItems" + Styles="@(new() { Item = "color: green;", SelectedItem = "color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;" })" /> + </div> + </div> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> <div>Use BitBreadcrumb in right-to-left (RTL).</div> <br /> @@ -153,4 +200,34 @@ <BitBreadcrumb Dir="BitDir.Rtl" Items="RtlBreadcrumbItems" MaxDisplayedItems="3" OverflowIndex="2" /> </div> </ExamplePreview> -</ComponentExampleBox> \ No newline at end of file +</ComponentExampleBox> + +@code { + private readonly List<BitBreadcrumbItem> BreadcrumbItemTemplateItems = + [ + new() + { + Text = "Item 1", Href = "/components/breadcrumb", + Template = (item => @<div style="color:green">@item.Text</div>), + OverflowTemplate = (item => @<div style="color:green;text-decoration:underline;">@item.Text</div>) + }, + new () + { + Text = "Item 2", Href = "/components/breadcrumb", + Template = (item => @<div style="color:yellow">@item.Text</div>), + OverflowTemplate = (item => @<div style="color:yellow;text-decoration:underline;">@item.Text</div>) + }, + new() + { + Text = "Item 3", Href = "/components/breadcrumb", + Template = (item => @<div style="color:red">@item.Text</div>), + OverflowTemplate = (item => @<div style="color:red;text-decoration:underline;">@item.Text</div>) + }, + new() + { + Text = "Item 4", Href = "/components/breadcrumb", IsSelected = true, + Template = (item => @<div style="color:blue">@item.Text</div>), + OverflowTemplate = (item => @<div style="color:blue;text-decoration:underline;">@item.Text</div>) + } + ]; +} \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.cs index 1415f83883..0413f86f7d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.cs @@ -23,20 +23,28 @@ public partial class _BitBreadcrumbItemDemo new() { Text = "Item 4", Href = "/components/breadcrumb", IsSelected = true } ]; + private readonly List<BitBreadcrumbItem> BreadcrumbItemsWitIcon = + [ + new() { Text = "Item 1", Href = "/components/breadcrumb", IconName = BitIconName.AdminELogoInverse32 }, + new() { Text = "Item 2", Href = "/components/breadcrumb", IconName = BitIconName.AppsContent }, + new() { Text = "Item 3", Href = "/components/breadcrumb", IconName = BitIconName.AzureIcon }, + new() { Text = "Item 4", Href = "/components/breadcrumb", IsSelected = true, IconName = BitIconName.ClassNotebookLogo16 } + ]; + private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithClass = [ - new() { Text = "Item 1", Href = "/components/breadcrumb", Class = "custom-item" }, - new() { Text = "Item 2", Href = "/components/breadcrumb", Class = "custom-item" }, - new() { Text = "Item 3", Href = "/components/breadcrumb", Class = "custom-item" }, - new() { Text = "Item 4", Href = "/components/breadcrumb", Class = "custom-item", IsSelected = true } + new() { Text = "Item 1", Href = "/components/breadcrumb", Class = "custom-item-1" }, + new() { Text = "Item 2", Href = "/components/breadcrumb", Class = "custom-item-2" }, + new() { Text = "Item 3", Href = "/components/breadcrumb", Class = "custom-item-1" }, + new() { Text = "Item 4", Href = "/components/breadcrumb", Class = "custom-item-2", IsSelected = true } ]; private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithStyle = [ new() { Text = "Item 1", Href = "/components/breadcrumb", Style = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, - new() { Text = "Item 2", Href = "/components/breadcrumb", Style = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, + new() { Text = "Item 2", Href = "/components/breadcrumb", Style = "color: aqua; text-shadow: aqua 0 0 1rem;" }, new() { Text = "Item 3", Href = "/components/breadcrumb", Style = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" }, - new() { Text = "Item 4", Href = "/components/breadcrumb", Style = "color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;", IsSelected = true } + new() { Text = "Item 4", Href = "/components/breadcrumb", Style = "color: aqua; text-shadow: aqua 0 0 1rem;", IsSelected = true } ]; private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithControlled = diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.samples.cs index 3e61fbdd35..e265ef43ec 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbItemDemo.razor.samples.cs @@ -47,58 +47,51 @@ public partial class _BitBreadcrumbItemDemo ];"; private readonly string example3RazorCode = @" -<BitBreadcrumb Items=""BreadcrumbItems"" - MaxDisplayedItems=""3"" +<BitBreadcrumb Items=""BreadcrumbItemsWitIcon"" + DividerIconName=""@BitIconName.CaretRightSolid8"" + OverflowIconName=""@BitIconName.ChevronDown"" OverflowIndex=""2"" - OverflowIcon=""@BitIconName.ChevronDown"" /> + MaxDisplayedItems=""3"" /> -<BitBreadcrumb Items=""BreadcrumbItems"" +<BitBreadcrumb Items=""BreadcrumbItemsWitIcon"" + OverflowIconName=""@BitIconName.CollapseMenu"" MaxDisplayedItems=""3"" OverflowIndex=""2"" - OverflowIcon=""@BitIconName.CollapseMenu"" />"; + ReversedIcon />"; private readonly string example3CsharpCode = @" -private readonly List<BitBreadcrumbItem> BreadcrumbItems = +private readonly List<BitBreadcrumbItem> BreadcrumbItemsWitIcon = [ - new() { Text = ""Item 1"", Href = ""/components/breadcrumb"" }, - new() { Text = ""Item 2"", Href = ""/components/breadcrumb"" }, - new() { Text = ""Item 3"", Href = ""/components/breadcrumb"" }, - new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", IsSelected = true } + new() { Text = ""Item 1"", Href = ""/components/breadcrumb"", IconName = BitIconName.AdminELogoInverse32 }, + new() { Text = ""Item 2"", Href = ""/components/breadcrumb"", IconName = BitIconName.AppsContent }, + new() { Text = ""Item 3"", Href = ""/components/breadcrumb"", IconName = BitIconName.AzureIcon }, + new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", IsSelected = true, IconName = BitIconName.ClassNotebookLogo16 } ];"; private readonly string example4RazorCode = @" -<style> - .custom-item { - color: #ffcece; - } - - .custom-item:hover { - color: #ff6868; - background: transparent; - } - - - .custom-selected-item { - color: blueviolet; - } - - .custom-selected-item:hover { - color: blueviolet; - background: transparent; - text-shadow: blueviolet 0 0 1rem; - } -</style> - - -<BitBreadcrumb Items=""BreadcrumbItemsWithClass"" /> - -<BitBreadcrumb Items=""BreadcrumbItemsWithStyle"" /> - - -<BitBreadcrumb Items=""BreadcrumbItems"" - SelectedItemClass=""custom-selected-item"" /> - -<BitBreadcrumb Items=""BreadcrumbItems"" - SelectedItemStyle=""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"" />"; +<BitBreadcrumb Items=""BreadcrumbItems""> + <DividerIconTemplate> + <BitIcon IconName=""@BitIconName.CaretRightSolid8"" Color=""BitColor.Warning"" /> + </DividerIconTemplate> +</BitBreadcrumb> + +<BitBreadcrumb Items=""BreadcrumbItems"" + MaxDisplayedItems=""3"" + OverflowIndex=""2""> + <ItemTemplate Context=""item""> + <div style=""font-weight: bold; color: #d13438; font-style:italic;""> + @item.Text + </div> + </ItemTemplate> + <OverflowTemplate Context=""item""> + <div style=""font-weight: bold; color: blueviolet; font-style:italic;""> + @item.Text + </div> + </OverflowTemplate> +</BitBreadcrumb> + +<BitBreadcrumb Items=""BreadcrumbItemTemplateItems"" + MaxDisplayedItems=""3"" + OverflowIndex=""2"" />"; private readonly string example4CsharpCode = @" private readonly List<BitBreadcrumbItem> BreadcrumbItems = [ @@ -108,20 +101,32 @@ public partial class _BitBreadcrumbItemDemo new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", IsSelected = true } ]; -private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithClass = -[ - new() { Text = ""Item 1"", Href = ""/components/breadcrumb"", Class = ""custom-item"" }, - new() { Text = ""Item 2"", Href = ""/components/breadcrumb"", Class = ""custom-item"" }, - new() { Text = ""Item 3"", Href = ""/components/breadcrumb"", Class = ""custom-item"" }, - new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", Class = ""custom-item"", IsSelected = true } -]; - -private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithStyle = +private readonly List<BitBreadcrumbItem> BreadcrumbItemTemplateItems = [ - new() { Text = ""Item 1"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Text = ""Item 2"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Text = ""Item 3"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, - new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"", IsSelected = true } + new() + { + Text = ""Item 1"", Href = ""/components/breadcrumb"", + Template = (item => @<div style=""color:green"">@item.Text</div>), + OverflowTemplate = (item => @<div style=""color:green;text-decoration:underline;"">@item.Text</div>) + }, + new () + { + Text = ""Item 2"", Href = ""/components/breadcrumb"", + Template = (item => @<div style=""color:yellow"">@item.Text</div>), + OverflowTemplate = (item => @<div style=""color:yellow;text-decoration:underline;"">@item.Text</div>) + }, + new() + { + Text = ""Item 3"", Href = ""/components/breadcrumb"", + Template = (item => @<div style=""color:red"">@item.Text</div>), + OverflowTemplate = (item => @<div style=""color:red;text-decoration:underline;"">@item.Text</div>) + }, + new() + { + Text = ""Item 4"", Href = ""/components/breadcrumb"", IsSelected = true, + Template = (item => @<div style=""color:blue"">@item.Text</div>), + OverflowTemplate = (item => @<div style=""color:blue;text-decoration:underline;"">@item.Text</div>) + } ];"; private readonly string example5RazorCode = @" @@ -129,7 +134,7 @@ public partial class _BitBreadcrumbItemDemo MaxDisplayedItems=""3"" OverflowIndex=""2"" OnItemClick=""(BitBreadcrumbItem item) => HandleOnItemClick(item)"" - SelectedItemStyle=""color: dodgerblue;"" />"; + Styles=""@(new() { SelectedItem = ""color: dodgerblue;"", OverflowSelectedItem = ""color: red;"" })"" />"; private readonly string example5CsharpCode = @" private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithControlled = [ @@ -203,8 +208,93 @@ private void RemoveCustomItem() }"; private readonly string example7RazorCode = @" -<BitBreadcrumb Dir=""BitDir.Rtl"" Items=""RtlBreadcrumbItems"" MaxDisplayedItems=""3"" OverflowIndex=""2"" />"; +<style> + .custom-class { + font-style: italic; + text-shadow: dodgerblue 0 0 0.5rem; + border-bottom: 1px solid dodgerblue; + } + + .custom-item { + color: #ffcece; + + &:hover { + color: #ff6868; + background: transparent; + } + } + + .custom-item-1 { + color: #b6ff00; + + &:hover { + color: #2aff00; + background: transparent; + } + } + + .custom-item-2 { + color: #ffd800; + + &:hover { + color: #ff6a00; + background: transparent; + } + } + + .custom-selected-item { + color: blueviolet; + + &:hover { + color: blueviolet; + background: transparent; + text-shadow: blueviolet 0 0 1rem; + } + } +</style> + + +<BitBreadcrumb Items=""BreadcrumbItems"" Class=""custom-class"" /> + +<BitBreadcrumb Items=""BreadcrumbItems"" Style=""font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;"" /> + +<BitBreadcrumb Items=""BreadcrumbItemsWithClass"" /> + +<BitBreadcrumb Items=""BreadcrumbItemsWithStyle"" /> + +<BitBreadcrumb Items=""BreadcrumbItems"" + Classes=""@(new() { Item = ""custom-item"", SelectedItem = ""custom-selected-item"" })"" /> + +<BitBreadcrumb Items=""BreadcrumbItems"" + Styles=""@(new() { Item = ""color: green;"", SelectedItem = ""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"" })"" />"; private readonly string example7CsharpCode = @" +private readonly List<BitBreadcrumbItem> BreadcrumbItems = +[ + new() { Text = ""Item 1"", Href = ""/components/breadcrumb"" }, + new() { Text = ""Item 2"", Href = ""/components/breadcrumb"" }, + new() { Text = ""Item 3"", Href = ""/components/breadcrumb"" }, + new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", IsSelected = true } +]; + +private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithClass = +[ + new() { Text = ""Item 1"", Href = ""/components/breadcrumb"", Class = ""custom-item-1"" }, + new() { Text = ""Item 2"", Href = ""/components/breadcrumb"", Class = ""custom-item-2"" }, + new() { Text = ""Item 3"", Href = ""/components/breadcrumb"", Class = ""custom-item-1"" }, + new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", Class = ""custom-item-2"", IsSelected = true } +]; + +private readonly List<BitBreadcrumbItem> BreadcrumbItemsWithStyle = +[ + new() { Text = ""Item 1"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, + new() { Text = ""Item 2"", Href = ""/components/breadcrumb"", Style = ""color: aqua; text-shadow: aqua 0 0 1rem;"" }, + new() { Text = ""Item 3"", Href = ""/components/breadcrumb"", Style = ""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" }, + new() { Text = ""Item 4"", Href = ""/components/breadcrumb"", Style = ""color: aqua; text-shadow: aqua 0 0 1rem;"", IsSelected = true } +];"; + + private readonly string example8RazorCode = @" +<BitBreadcrumb Dir=""BitDir.Rtl"" Items=""RtlBreadcrumbItems"" MaxDisplayedItems=""3"" OverflowIndex=""2"" />"; + private readonly string example8CsharpCode = @" private readonly List<BitBreadcrumbItem> RtlBreadcrumbItems = [ new() { Text = ""پوشه اول"" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor index f6c889a642..056a50cb92 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor @@ -100,74 +100,97 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="OverflowIcon" RazorCode="@example3RazorCode" Id="example3"> +<ComponentExampleBox Title="Icons" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> - <div>Customize the overflow icon in BitBreadcrumb using different icon name values.</div> + <div>Customize the overflow icon, divider icon and item icons in BitBreadcrumb using different icon name values.</div> <br /><br /> <div class="example-box"> <div> - <div>BitIconName (ChevronDown)</div> - <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" OverflowIconName="@BitIconName.ChevronDown"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + <div>BitIconName (OverflowIcon: ChevronDown - DividerIcon: CaretRightSolid8)</div> + <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" DividerIconName="@BitIconName.CaretRightSolid8" OverflowIconName="@BitIconName.ChevronDown"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" IconName="@BitIconName.AdminELogoInverse32" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" IconName="@BitIconName.AppsContent" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" IconName="@BitIconName.AzureIcon" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IconName="@BitIconName.ClassNotebookLogo16" IsSelected /> </BitBreadcrumb> </div> <br /> <div> - <div>BitIconName (CollapseMenu)</div> - <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" OverflowIconName="@BitIconName.CollapseMenu"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + <div>BitIconName (OverflowIcon: CollapseMenu) with active ReversedIcon</div> + <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" OverflowIconName="@BitIconName.CollapseMenu" ReversedIcon> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" IconName="@BitIconName.AdminELogoInverse32" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" IconName="@BitIconName.AppsContent" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" IconName="@BitIconName.AzureIcon" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IconName="@BitIconName.ClassNotebookLogo16" IsSelected /> </BitBreadcrumb> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Class & Style" RazorCode="@example4RazorCode" Id="example4"> +<ComponentExampleBox Title="Templates" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> - <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <div>Here are some examples of customizing the breadcrumb content.</div> <br /><br /> <div class="example-box"> <div> - <div>Options Class</div> + <div>DividerIconTemplate</div> <BitBreadcrumb TItem="BitBreadcrumbOption"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" Class="custom-item" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" Class="custom-item" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" Class="custom-item" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" Class="custom-item" IsSelected /> + <DividerIconTemplate> + <BitIcon IconName="@BitIconName.CaretRightSolid8" Color="BitColor.Warning" /> + </DividerIconTemplate> + <Options> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </Options> </BitBreadcrumb> </div> + <br /> <div> - <div>Options Style</div> - <BitBreadcrumb TItem="BitBreadcrumbOption"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" IsSelected /> + <div>ItemTemplate & OverflowTemplate</div> + <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2"> + <ItemTemplate Context="item"> + <div style="font-weight: bold; color: #d13438; font-style:italic;"> + @item.Text + </div> + </ItemTemplate> + <OverflowTemplate Context="item"> + <div style="font-weight: bold; color: blueviolet; font-style:italic;"> + @item.Text + </div> + </OverflowTemplate> + <Options> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </Options> </BitBreadcrumb> </div> <br /> <div> - <div>Selected Option Class</div> + <div>Item's template & Item's overflow tTemplate</div> <BitBreadcrumb TItem="BitBreadcrumbOption" SelectedItemClass="custom-selected-item"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> - </BitBreadcrumb> - </div> - <div> - <div>Selected Option Style</div> - <BitBreadcrumb TItem="BitBreadcrumbOption" SelectedItemStyle="color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"> - <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> - <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + <Options> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb"> + <Template Context="item"><div style="color:green">@item.Text</div></Template> + <OverflowTemplate Context="item"><div style="color:green;text-decoration:underline;">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb"> + <Template Context="item"><div style="color:yellow">@item.Text</div></Template> + <OverflowTemplate Context="item"><div style="color:yellow;text-decoration:underline;">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb"> + <Template Context="item"><div style="color:red">@item.Text</div></Template> + <OverflowTemplate Context="item"><div style="color:red;text-decoration:underline;">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected> + <Template Context="item"><div style="color:blue">@item.Text</div></Template> + <OverflowTemplate Context="item"><div style="color:blue;text-decoration:underline;">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + </Options> </BitBreadcrumb> </div> </div> @@ -179,7 +202,7 @@ <div>By clicking on the options, the Selected option will change and match the SelectedItemStyle.</div> <br /><br /> <div class="example-box"> - <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" SelectedItemStyle="color: dodgerblue;"> + <BitBreadcrumb TItem="BitBreadcrumbOption" MaxDisplayedItems="3" OverflowIndex="2" Styles="@(new() { SelectedItem = "color: dodgerblue;", OverflowSelectedItem = "color: red;" })"> <BitBreadcrumbOption Text="Option 1" IsSelected="@(SelectedOptionNumber == 1)" OnClick="() => SelectedOptionNumber = 1" /> <BitBreadcrumbOption Text="Option 2" IsSelected="@(SelectedOptionNumber == 2)" OnClick="() => SelectedOptionNumber = 2" /> <BitBreadcrumbOption Text="Option 3" IsSelected="@(SelectedOptionNumber == 3)" OnClick="() => SelectedOptionNumber = 3" /> @@ -222,7 +245,66 @@ </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7RazorCode" Id="example7"> +<ComponentExampleBox Title="Class & Style" RazorCode="@example7RazorCode" Id="example7"> + <ExamplePreview> + <div>Customize the appearance of BitBreadcrumb using styles and CSS classes.</div> + <br /><br /> + <div class="example-box"> + <div> + <div>Component's Style & Class:</div> + <BitBreadcrumb TItem="BitBreadcrumbOption" Class="custom-class"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </BitBreadcrumb> + <br /> + <BitBreadcrumb TItem="BitBreadcrumbOption" Style="font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </BitBreadcrumb> + </div> + <br /> + <div> + <div>Option's Style & Class:</div> + <BitBreadcrumb TItem="BitBreadcrumbOption"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" Class="custom-item-1" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" Class="custom-item-2" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" Class="custom-item-1" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" Class="custom-item-2" IsSelected /> + </BitBreadcrumb> + <br /> + <BitBreadcrumb TItem="BitBreadcrumbOption"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" Style="color: aqua; text-shadow: aqua 0 0 1rem;" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" Style="color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" Style="color: aqua; text-shadow: aqua 0 0 1rem;" IsSelected /> + </BitBreadcrumb> + </div> + <br /> + <div> + <div>Styles & Classes:</div> + <BitBreadcrumb TItem="BitBreadcrumbOption" Classes="@(new() { Item = "custom-item", SelectedItem = "custom-selected-item" })"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </BitBreadcrumb> + <br /> + <BitBreadcrumb TItem="BitBreadcrumbOption" Styles="@(new() { Item = "color: green;", SelectedItem = "color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;" })"> + <BitBreadcrumbOption Text="Option 1" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 2" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 3" Href="/components/breadcrumb" /> + <BitBreadcrumbOption Text="Option 4" Href="/components/breadcrumb" IsSelected /> + </BitBreadcrumb> + </div> + </div> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" Id="example8"> <ExamplePreview> <div>Use BitBreadcrumb in right-to-left (RTL).</div> <br /> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor.samples.cs index 6e65d8587b..2c3515dd1c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Breadcrumb/_BitBreadcrumbOptionDemo.razor.samples.cs @@ -68,75 +68,76 @@ public partial class _BitBreadcrumbOptionDemo </BitBreadcrumb>"; private readonly string example3RazorCode = @" -<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" OverflowIcon=""@BitIconName.ChevronDown""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" DividerIconName=""@BitIconName.CaretRightSolid8"" OverflowIconName=""@BitIconName.ChevronDown""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AdminELogoInverse32"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AppsContent"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AzureIcon"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IconName=""@BitIconName.ClassNotebookLogo16"" IsSelected /> </BitBreadcrumb> -<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" OverflowIcon=""@BitIconName.CollapseMenu""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" OverflowIconName=""@BitIconName.CollapseMenu"" ReversedIcon> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AdminELogoInverse32"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AppsContent"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" IconName=""@BitIconName.AzureIcon"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IconName=""@BitIconName.ClassNotebookLogo16"" IsSelected /> </BitBreadcrumb>"; private readonly string example4RazorCode = @" -<style> - .custom-item { - color: #ffcece; - } - - .custom-item:hover { - color: #ff6868; - background: transparent; - } - - - .custom-selected-item { - color: blueviolet; - } - - .custom-selected-item:hover { - color: blueviolet; - background: transparent; - text-shadow: blueviolet 0 0 1rem; - } -</style> - - <BitBreadcrumb TItem=""BitBreadcrumbOption""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" Class=""custom-item"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" Class=""custom-item"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" Class=""custom-item"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" Class=""custom-item"" IsSelected /> + <DividerIconTemplate> + <BitIcon IconName=""@BitIconName.CaretRightSolid8"" Color=""BitColor.Warning"" /> + </DividerIconTemplate> + <Options> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> + </Options> </BitBreadcrumb> -<BitBreadcrumb TItem=""BitBreadcrumbOption""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" IsSelected /> +<div>ItemTemplate & OverflowTemplate</div> +<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2""> + <ItemTemplate Context=""item""> + <div style=""font-weight: bold; color: #d13438; font-style:italic;""> + @item.Text + </div> + </ItemTemplate> + <OverflowTemplate Context=""item""> + <div style=""font-weight: bold; color: blueviolet; font-style:italic;""> + @item.Text + </div> + </OverflowTemplate> + <Options> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> + </Options> </BitBreadcrumb> - - + <BitBreadcrumb TItem=""BitBreadcrumbOption"" SelectedItemClass=""custom-selected-item""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> -</BitBreadcrumb> - -<BitBreadcrumb TItem=""BitBreadcrumbOption"" SelectedItemStyle=""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;""> - <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> - <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> + <Options> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb""> + <Template Context=""item""><div style=""color:green"">@item.Text</div></Template> + <OverflowTemplate Context=""item""><div style=""color:green;text-decoration:underline;"">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb""> + <Template Context=""item""><div style=""color:yellow"">@item.Text</div></Template> + <OverflowTemplate Context=""item""><div style=""color:yellow;text-decoration:underline;"">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb""> + <Template Context=""item""><div style=""color:red"">@item.Text</div></Template> + <OverflowTemplate Context=""item""><div style=""color:red;text-decoration:underline;"">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected> + <Template Context=""item""><div style=""color:blue"">@item.Text</div></Template> + <OverflowTemplate Context=""item""><div style=""color:blue;text-decoration:underline;"">@item.Text</div></OverflowTemplate> + </BitBreadcrumbOption> + </Options> </BitBreadcrumb>"; private readonly string example5RazorCode = @" -<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" SelectedItemStyle=""color: dodgerblue;""> +<BitBreadcrumb TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2"" Styles=""@(new() { SelectedItem = ""color: dodgerblue;"", OverflowSelectedItem = ""color: red;"" })""> <BitBreadcrumbOption Text=""Option 1"" IsSelected=""@(SelectedOptionNumber == 1)"" OnClick=""() => SelectedOptionNumber = 1"" /> <BitBreadcrumbOption Text=""Option 2"" IsSelected=""@(SelectedOptionNumber == 2)"" OnClick=""() => SelectedOptionNumber = 2"" /> <BitBreadcrumbOption Text=""Option 3"" IsSelected=""@(SelectedOptionNumber == 3)"" OnClick=""() => SelectedOptionNumber = 3"" /> @@ -170,6 +171,95 @@ public partial class _BitBreadcrumbOptionDemo private int CustomizedSelectedOptionNumber = 4;"; private readonly string example7RazorCode = @" +<style> + .custom-class { + font-style: italic; + text-shadow: dodgerblue 0 0 0.5rem; + border-bottom: 1px solid dodgerblue; + } + + .custom-item { + color: #ffcece; + + &:hover { + color: #ff6868; + background: transparent; + } + } + + .custom-item-1 { + color: #b6ff00; + + &:hover { + color: #2aff00; + background: transparent; + } + } + + .custom-item-2 { + color: #ffd800; + + &:hover { + color: #ff6a00; + background: transparent; + } + } + + .custom-selected-item { + color: blueviolet; + + &:hover { + color: blueviolet; + background: transparent; + text-shadow: blueviolet 0 0 1rem; + } + } +</style> + + +<BitBreadcrumb TItem=""BitBreadcrumbOption"" Class=""custom-class""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +</BitBreadcrumb> + +<BitBreadcrumb TItem=""BitBreadcrumbOption"" Style=""font-style: italic;text-shadow: aqua 0 0 0.5rem;border-bottom: 1px solid aqua;""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +</BitBreadcrumb> + +<BitBreadcrumb TItem=""BitBreadcrumbOption""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" Class=""custom-item-1"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" Class=""custom-item-2"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" Class=""custom-item-1"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" Class=""custom-item-2"" IsSelected /> +</BitBreadcrumb> + +<BitBreadcrumb TItem=""BitBreadcrumbOption""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" Style=""color: aqua; text-shadow: aqua 0 0 1rem;"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" Style=""color: dodgerblue; text-shadow: dodgerblue 0 0 1rem;"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" Style=""color: aqua; text-shadow: aqua 0 0 1rem;"" IsSelected /> +</BitBreadcrumb> + +<BitBreadcrumb TItem=""BitBreadcrumbOption"" Classes=""@(new() { Item = ""custom-item"", SelectedItem = ""custom-selected-item"" })""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +</BitBreadcrumb> + +<BitBreadcrumb TItem=""BitBreadcrumbOption"" Styles=""@(new() { Item = ""color: green;"", SelectedItem = ""color: lightseagreen; text-shadow: lightseagreen 0 0 1rem;"" })""> + <BitBreadcrumbOption Text=""Option 1"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 2"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 3"" Href=""/components/breadcrumb"" /> + <BitBreadcrumbOption Text=""Option 4"" Href=""/components/breadcrumb"" IsSelected /> +</BitBreadcrumb>"; + + private readonly string example8RazorCode = @" <BitBreadcrumb Dir=""BitDir.Rtl"" TItem=""BitBreadcrumbOption"" MaxDisplayedItems=""3"" OverflowIndex=""2""> <BitBreadcrumbOption Text=""پوشه اول"" /> <BitBreadcrumbOption Text=""پوشه دوم"" IsSelected /> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor index 55d275a42e..93484ff7ff 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor @@ -53,7 +53,21 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Template" RazorCode="@example3RazorCode" Id="example3"> + <ComponentExampleBox Title="Responsive" RazorCode="@example3RazorCode" Id="example3"> + <ExamplePreview> + <div>Rendering the callout in responsive mode on small screens.</div><br /> + <BitDropMenu Text="Responsive" Responsive> + <BitStack Gap="1rem" Style="padding:0.5rem"> + <BitText Typography="BitTypography.Subtitle1">This is the content</BitText> + <BitText Typography="BitTypography.Subtitle1">This is the content</BitText> + <BitText Typography="BitTypography.Subtitle1">This is the content</BitText> + <BitText Typography="BitTypography.Subtitle1">This is the content</BitText> + </BitStack> + </BitDropMenu> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Template" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> <div>Here is an example of customizing the drop menu.</div><br /> <BitDropMenu Text="Add Icon" IconName="@BitIconName.Emoji2"> @@ -73,7 +87,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Events" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ComponentExampleBox Title="Events" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> <div>Utilizing the drop menu OnClick event:</div><br /> <BitDropMenu Text="@($"Click me ({clickCounter})")" OnClick="() => clickCounter++"> @@ -84,7 +98,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example5RazorCode" Id="example5"> + <ComponentExampleBox Title="Style & Class" RazorCode="@example6RazorCode" Id="example6"> <ExamplePreview> <div>Further customization by overriding default styles and classes, allowing tailored design modifications to suit specific UI requirements.</div> <br /><br /> @@ -126,7 +140,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example6RazorCode" Id="example6"> + <ComponentExampleBox Title="RTL" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> <div>Use BitDropMenu in right-to-left (RTL).</div> <br /> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.cs index 0b95b0799a..88c531af04 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.cs @@ -56,6 +56,20 @@ public partial class BitDropMenuDemo Description = "The callback is called when the drop menu is clicked." }, new() + { + Name = "OnDismiss", + Type = "EventCallback", + DefaultValue = "", + Description = "The callback is called when the drop menu is dismissed." + }, + new() + { + Name = "Responsive", + Type = "bool", + DefaultValue = "false", + Description = "Renders the drop menu in responsive mode on small screens." + }, + new() { Name = "Styles", Type = "BitDropMenuClassStyles?", @@ -110,6 +124,13 @@ public partial class BitDropMenuDemo Description = "Custom CSS classes/styles for the opened callout state of the BitDropMenu." }, new() + { + Name = "Button", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the button of the BitDropMenu." + }, + new() { Name = "Icon", Type = "string?", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.samples.cs index b4f1b0847e..19f478b725 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/DropMenu/BitDropMenuDemo.razor.samples.cs @@ -41,6 +41,16 @@ public partial class BitDropMenuDemo </BitDropMenu>"; private readonly string example3RazorCode = @" +<BitDropMenu Text=""Responsive"" Responsive> + <BitStack Gap=""1rem"" Style=""padding:0.5rem""> + <BitText Typography=""BitTypography.Subtitle1"">This is the content</BitText> + <BitText Typography=""BitTypography.Subtitle1"">This is the content</BitText> + <BitText Typography=""BitTypography.Subtitle1"">This is the content</BitText> + <BitText Typography=""BitTypography.Subtitle1"">This is the content</BitText> + </BitStack> +</BitDropMenu>"; + + private readonly string example4RazorCode = @" <BitDropMenu Text=""Add Icon"" IconName=""@BitIconName.Emoji2""> <Template> <div style=""display:flex;gap:10px;""> @@ -56,16 +66,16 @@ public partial class BitDropMenuDemo </Body> </BitDropMenu>"; - private readonly string example4RazorCode = @" + private readonly string example5RazorCode = @" <BitDropMenu Text=""@($""Click me ({clickCounter})"")"" OnClick=""() => clickCounter++""> <BitStack Gap=""1rem"" Style=""padding:0.5rem""> <BitText Typography=""BitTypography.Subtitle1"">This is the content</BitText> </BitStack> </BitDropMenu>"; - private readonly string example4CsharpCode = @" + private readonly string example5CsharpCode = @" private int clickCounter;"; - private readonly string example5RazorCode = @" + private readonly string example6RazorCode = @" <style> .custom-class { border-radius: 1rem; @@ -131,7 +141,7 @@ public partial class BitDropMenuDemo </BitStack> </BitDropMenu>"; - private readonly string example6RazorCode = @" + private readonly string example7RazorCode = @" <BitDropMenu Text=""منو"" Dir=""BitDir.Rtl""> <BitStack Gap=""1rem"" Style=""padding:0.5rem""> <BitText Typography=""BitTypography.Subtitle1"">این یک محتوای تستی می باشد.</BitText> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor index dad0e86aff..4cddad2188 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor @@ -8,6 +8,7 @@ <ComponentDemo ComponentName="Nav" ComponentDescription="A navigation pane (Nav) provides links to the main areas of an app or site." ComponentParameters="componentParameters" + ComponentPublicMembers="componentPublicMembers" ComponentSubClasses="componentSubClasses" ComponentSubEnums="componentSubEnums" Notes="The BitNav is a Multi-API component which can accept the list of Items in 3 different ways: BitNavItem class, a custom Generic class, and BitNavOption component."> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.cs index 5b106ce113..97d6ba0902 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.cs @@ -4,6 +4,13 @@ public partial class BitNavDemo { private readonly List<ComponentParameter> componentParameters = [ + new() + { + Name = "Accent", + Type = "BitColor?", + DefaultValue = "null", + Description = "The accent color of the nav.", + }, new() { Name = "ChevronDownIcon", @@ -24,8 +31,15 @@ public partial class BitNavDemo Type = "BitNavClassStyles?", DefaultValue = "null", Description = "Custom CSS classes for different parts of the BitNav component.", + LinkType = LinkType.Link, Href = "#nav-class-styles", - LinkType = LinkType.Link + }, + new() + { + Name = "Color", + Type = "BitColor?", + DefaultValue = "null", + Description = "The general color of the nav.", }, new() { @@ -35,6 +49,20 @@ public partial class BitNavDemo Description = "The initially selected item in manual mode." }, new() + { + Name = "FitWidth", + Type = "bool", + DefaultValue = "false", + Description = "Renders the nav in a width to only fit its content." + }, + new() + { + Name = "FullWidth", + Type = "bool", + DefaultValue = "false", + Description = "Renders the nav in full width of its container element." + }, + new() { Name = "HeaderTemplate", Type = "RenderFragment<TItem>?", @@ -49,6 +77,13 @@ public partial class BitNavDemo Description = "The render mode of the custom HeaderTemplate." }, new() + { + Name = "IconOnly", + Type = "bool", + DefaultValue = "false", + Description = "Only renders the icon of each nav item." + }, + new() { Name = "IndentValue", Type = "int", @@ -74,9 +109,9 @@ public partial class BitNavDemo Name = "Items", Type = "IList<TItem>", DefaultValue = "new List<TItem>()", - Href="#nav-item", + Description = "A collection of item to display in the navigation bar.", LinkType = LinkType.Link, - Description = "A collection of item to display in the navigation bar." + Href="#nav-item", }, new() { @@ -173,10 +208,25 @@ public partial class BitNavDemo Type = "BitNavClassStyles?", DefaultValue = "null", Description = "Custom CSS styles for different parts of the BitNav component.", + LinkType = LinkType.Link, Href = "#nav-class-styles", - LinkType = LinkType.Link } ]; + private readonly List<ComponentParameter> componentPublicMembers = + [ + new() + { + Name = "CollapseAll", + Type = "Action", + Description = "Collapses all items and children.", + }, + new() + { + Name = "ToggleItem", + Type = "Func<Task, TItem>", + Description = "Toggles an item.", + }, + ]; private readonly List<ComponentSubClass> componentSubClasses = [ new() @@ -666,8 +716,8 @@ public partial class BitNavDemo { Id = "nav-class-styles", Title = "BitNavClassStyles", - Parameters = new() - { + Parameters = + [ new() { Name = "Root", @@ -704,6 +754,13 @@ public partial class BitNavDemo Description = "Custom CSS classes/styles for the item icon of the BitNav." }, new() + { + Name = "ItemText", + Type = "String?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the item text of the BitNav." + }, + new() { Name = "SelectedItemContainer", Type = "String?", @@ -724,7 +781,7 @@ public partial class BitNavDemo DefaultValue = "null", Description = "Custom CSS classes/styles for the separator of the BitNav." }, - } + ] } ]; private readonly List<ComponentSubEnum> componentSubEnums = diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.scss index 05ba77a069..838ccf1540 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitNavDemo.razor.scss @@ -1,40 +1,7 @@ @import '../../../../Styles/abstracts/_media-queries.scss'; ::deep { - a { - text-decoration: none; - } - - .nav-custom-header { - color: green; - font-size: 17px; - font-weight: 600; - } - - .nav-custom-item { - gap: 4px; - display: flex; - color: #ff7800; - font-weight: 600; - align-items: center; - flex-flow: row nowrap; - } - .example-box { - width: 350px; - display: flex; - flex-direction: column; - - @include sm { - width: 250px; - } - - .margin-top { - margin-top: 20px; - } - } - - .events-example-box { gap: 100px; display: flex; flex-direction: row; @@ -53,10 +20,24 @@ width: 250px; } } + } - .flex { - display: flex; - flex-direction: column; - } + a { + text-decoration: none; + } + + .nav-custom-header { + color: green; + font-size: 17px; + font-weight: 600; + } + + .nav-custom-item { + gap: 4px; + display: flex; + color: #ff7800; + font-weight: 600; + align-items: center; + flex-flow: row nowrap; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitPlatformMenu.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/Section.cs similarity index 68% rename from src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitPlatformMenu.cs rename to src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/Section.cs index fd0a3292b3..bdbaabbbec 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/BitPlatformMenu.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/Section.cs @@ -1,13 +1,13 @@ namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Navs.Nav; -public class BitPlatformMenu +public class Section { public string Text { get; set; } = string.Empty; public string? Icon { get; set; } public string? Url { get; set; } public bool IsEnabled { get; set; } = true; public bool IsExpanded { get; set; } - public List<BitPlatformMenu> Links { get; set; } = []; + public List<Section> Links { get; set; } = []; public string? Comment { get; set; } - public RenderFragment<BitPlatformMenu>? Template { get; set; } + public RenderFragment<Section>? Template { get; set; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor index 1f55857165..d91ac4f28c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor @@ -1,168 +1,162 @@ -<ComponentExampleBox Title="Basic" RazorCode="@example1CustomItemRazorCode" CsharpCode="@example1CustomItemCsharpCode" Id="example1"> +<ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="CustomBitPlatformNavMenu" - NameSelectors="@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })" /> - </div> + <BitNav Items="CustomBitPlatformNavMenu" + NameSelectors="@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })" /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Grouped" RazorCode="@example2CustomItemRazorCode" CsharpCode="@example2CustomItemCsharpCode" Id="example2"> +<ComponentExampleBox Title="FitWidth" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="CustomCarNavMenu" - RenderType="BitNavRenderType.Grouped" - NameSelectors="@(new() { Text = { Name = nameof(CarMenu.Name) }, - Url = { Name = nameof(CarMenu.PageUrl) }, - Target = { Name = nameof(CarMenu.UrlTarget) }, - Title = { Name = nameof(CarMenu.Tooltip) }, - IsExpanded = { Name = nameof(CarMenu.IsExpandedParent) }, - CollapseAriaLabel = { Name = nameof(CarMenu.CollapsedAriaLabel) }, - ExpandAriaLabel = { Name = nameof(CarMenu.ExpandedAriaLabel) }, - ChildItems = { Name = nameof(CarMenu.Links) }, - Description = { Name = nameof(CarMenu.Comment) } })" /> - </div> + <BitNav Items="CustomBitPlatformNavMenu" FitWidth + NameSelectors="@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })" /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Manual Mode" RazorCode="@example3CustomItemRazorCode" CsharpCode="@example3CustomItemCsharpCode" Id="example3"> +<ComponentExampleBox Title="Grouped" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div class="example-box"> - <div> - <BitLabel>Basic</BitLabel> - <BitNav Items="CustomFoodNavMenu" - Mode="BitNavMode.Manual" - DefaultSelectedItem="CustomFoodNavMenu[0].Childs[2]" - NameSelectors="@(new() { Text = { Selector = item => item.Name }, - IconName = { Selector = item => item.Icon }, - ChildItems = { Selector = item => item.Childs }, - Description = { Selector = item => item.Comment } })" /> - </div> + <BitNav Items="CustomCarNavMenu" + RenderType="BitNavRenderType.Grouped" + NameSelectors="@(new() { Text = { Name = nameof(CarMenu.Name) }, + Url = { Name = nameof(CarMenu.PageUrl) }, + Target = { Name = nameof(CarMenu.UrlTarget) }, + Title = { Name = nameof(CarMenu.Tooltip) }, + IsExpanded = { Name = nameof(CarMenu.IsExpandedParent) }, + CollapseAriaLabel = { Name = nameof(CarMenu.CollapsedAriaLabel) }, + ExpandAriaLabel = { Name = nameof(CarMenu.ExpandedAriaLabel) }, + ChildItems = { Name = nameof(CarMenu.Links) }, + Description = { Name = nameof(CarMenu.Comment) } })" /> + </ExamplePreview> +</ComponentExampleBox> +<ComponentExampleBox Title="Manual Mode" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ExamplePreview> + <div>Basic</div><br /> + <BitNav Items="CustomFoodNavMenu" + Mode="BitNavMode.Manual" + DefaultSelectedItem="CustomFoodNavMenu[0].Childs[2]" + NameSelectors="@(new() { Text = { Selector = item => item.Name }, + IconName = { Selector = item => item.Icon }, + ChildItems = { Selector = item => item.Childs }, + Description = { Selector = item => item.Comment } })" /> + <br /><br /><br /> + <div>Two-Way Bind</div><br /> + <div class="example-box"> + <BitNav @bind-SelectedItem="CustomSelectedFood" + Items="CustomFoodNavMenu" + Mode="BitNavMode.Manual" + DefaultSelectedItem="CustomFoodNavMenu[0].Childs[2]" + NameSelectors="@(new() { Text = { Selector = item => item.Name }, + IconName = { Selector = item => item.Icon }, + ChildItems = { Selector = item => item.Childs }, + Description = { Selector = item => item.Comment } })" + OnSelectItem="(FoodMenu item) => CustomSelectedFoodName = FoodMenuDropdownItems.Single(i => i.Text == item.Name).Text" /> <br /> - <br /> - <br /> - - <div class="margin-top"> - <BitLabel>Two-Way Bind</BitLabel> - - <BitNav @bind-SelectedItem="CustomSelectedFood" - Items="CustomFoodNavMenu" - Mode="BitNavMode.Manual" - DefaultSelectedItem="CustomFoodNavMenu[0].Childs[2]" - NameSelectors="@(new() { Text = { Selector = item => item.Name }, - IconName = { Selector = item => item.Icon }, - ChildItems = { Selector = item => item.Childs }, - Description = { Selector = item => item.Comment } })" - OnSelectItem="(FoodMenu item) => CustomSelectedFoodName = FoodMenuDropdownItems.Single(i => i.Text == item.Name).Text" /> - - <BitDropdown @bind-Value="CustomSelectedFoodName" - Label="Select Item" - Items="FoodMenuDropdownItems" - OnSelectItem="(BitDropdownItem<string> item) => CustomSelectedFood = Flatten(CustomFoodNavMenu).Single(i => i.Name == item.Value)" /> - </div> + <BitDropdown @bind-Value="CustomSelectedFoodName" + FitWidth + Label="Select Item" + Items="FoodMenuDropdownItems" + OnSelectItem="(BitDropdownItem<string> item) => CustomSelectedFood = Flatten(CustomFoodNavMenu).Single(i => i.Name == item.Value)" /> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Templates" RazorCode="@example4CustomItemRazorCode" CsharpCode="@example4CustomItemCsharpCode" Id="example4"> +<ComponentExampleBox Title="IconOnly" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-box"> - <div> - <BitLabel>Header Template (Grouped)</BitLabel> - <BitNav Items="CustomCarNavMenu" - RenderType="BitNavRenderType.Grouped" - NameSelectors="@(new() { Text = { Name = nameof(CarMenu.Name) }, - Url = { Name = nameof(CarMenu.PageUrl) }, - Target = { Name = nameof(CarMenu.UrlTarget) }, - Title = { Name = nameof(CarMenu.Tooltip) }, - IsExpanded = { Name = nameof(CarMenu.IsExpandedParent) }, - CollapseAriaLabel = { Name = nameof(CarMenu.CollapsedAriaLabel) }, - ExpandAriaLabel = { Name = nameof(CarMenu.ExpandedAriaLabel) }, - ChildItems = { Name = nameof(CarMenu.Links) }, - Description = { Name = nameof(CarMenu.Comment) } })"> - <HeaderTemplate Context="item"> - <div class="nav-custom-header"> - <BitIcon IconName="@BitIconName.FavoriteStarFill" /> - <span>@item.Name</span> - </div> - </HeaderTemplate> - </BitNav> - </div> - - <br /> - <br /> - <br /> + <BitToggle @bind-Value="iconOnly" Label="Hide texts?" Inline /> + <br /> + <BitNav Items="CustomIconOnlyNavMenu" Mode="BitNavMode.Manual" IconOnly="iconOnly" + NameSelectors="@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })" /> + </ExamplePreview> +</ComponentExampleBox> - <div class="margin-top"> - <BitLabel>Item Template</BitLabel> - <BitNav Items="CustomFoodNavMenu" - Mode="BitNavMode.Manual" - NameSelectors="@(new() { Text = { Selector = item => item.Name }, - IconName = { Selector = item => item.Icon }, - ChildItems = { Selector = item => item.Childs }, - Description = { Selector = item => item.Comment } })"> - <ItemTemplate Context="item"> - <div class="nav-custom-item"> - <BitCheckbox /> - <BitIcon IconName="@item.Icon" /> - <span>@item.Name</span> - </div> - </ItemTemplate> - </BitNav> - </div> - </div> +<ComponentExampleBox Title="Custom Templates" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ExamplePreview> + <div>Header Template (Grouped)</div><br /> + <BitNav Items="CustomCarNavMenu" + RenderType="BitNavRenderType.Grouped" + NameSelectors="@(new() { Text = { Name = nameof(CarMenu.Name) }, + Url = { Name = nameof(CarMenu.PageUrl) }, + Target = { Name = nameof(CarMenu.UrlTarget) }, + Title = { Name = nameof(CarMenu.Tooltip) }, + IsExpanded = { Name = nameof(CarMenu.IsExpandedParent) }, + CollapseAriaLabel = { Name = nameof(CarMenu.CollapsedAriaLabel) }, + ExpandAriaLabel = { Name = nameof(CarMenu.ExpandedAriaLabel) }, + ChildItems = { Name = nameof(CarMenu.Links) }, + Description = { Name = nameof(CarMenu.Comment) } })"> + <HeaderTemplate Context="item"> + <div class="nav-custom-header"> + <BitIcon IconName="@BitIconName.FavoriteStarFill" /> + <span>@item.Name</span> + </div> + </HeaderTemplate> + </BitNav> + <br /><br /><br /> + <div>Item Template</div><br /> + <BitNav Items="CustomFoodNavMenu" + Mode="BitNavMode.Manual" + NameSelectors="@(new() { Text = { Selector = item => item.Name }, + IconName = { Selector = item => item.Icon }, + ChildItems = { Selector = item => item.Childs }, + Description = { Selector = item => item.Comment } })"> + <ItemTemplate Context="item"> + <div class="nav-custom-item"> + <BitCheckbox /> + <BitIcon IconName="@item.Icon" /> + <span>@item.Name</span> + </div> + </ItemTemplate> + </BitNav> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Events" RazorCode="@example5CustomItemRazorCode" CsharpCode="@example5CustomItemCsharpCode" Id="example5"> +<ComponentExampleBox Title="Events" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <div class="events-example-box"> + <div class="example-box"> <BitNav Items="CustomFoodNavMenu" Mode="BitNavMode.Manual" OnItemClick="(FoodMenu item) => CustomClickedItem = item" OnSelectItem="(FoodMenu item) => CustomSelectedItem = item" OnItemToggle="(FoodMenu item) => CustomToggledItem = item" NameSelectors="@(new() { Text = { Selector = item => item.Name }, - IconName = { Selector = item => item.Icon }, - ChildItems = { Selector = item => item.Childs }, - Description = { Selector = item => item.Comment } })" /> - <div class="flex"> - <span>Clicked Item: <b>@CustomClickedItem?.Name</b></span> - <span>Selected Item: <b>@CustomSelectedItem?.Name</b></span> + IconName = { Selector = item => item.Icon }, + ChildItems = { Selector = item => item.Childs }, + Description = { Selector = item => item.Comment } })" /> + <div> + <span>Clicked Item: <b>@CustomClickedItem?.Name</b></span><br /> + <span>Selected Item: <b>@CustomSelectedItem?.Name</b></span><br /> <span>Toggled Item: <b>@(CustomToggledItem is null ? "N/A" : $"{CustomToggledItem.Name} ({(CustomToggledItem.IsExpanded ? "Expanded" : "Collapsed")})")</b></span> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Styles" RazorCode="@example6CustomItemRazorCode" CsharpCode="@example6CustomItemCsharpCode" Id="example6"> +<ComponentExampleBox Title="Custom Styles" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="CustomCustomStyleNavMenu" - NameSelectors="@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })" - Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", - ToggleButton = "color: cyan;", - Item = "color: red;", - ItemIcon = "color: gold; margin-right: 15px;" })" /> - </div> + <BitNav Items="CustomCustomStyleNavMenu" + NameSelectors="@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })" + Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", + ToggleButton = "color: cyan;", + Item = "color: red;", + ItemIcon = "color: gold; margin-right: 15px;" })" /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7CustomItemRazorCode" CsharpCode="@example7CustomItemCsharpCode" Id="example7"> +<ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> <ExamplePreview> <div dir="rtl"> - <div class="example-box"> - <BitNav Dir="BitDir.Rtl" - Items="CustomRtlBitPlatformNavMenu" - NameSelectors="@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })" /> - </div> + <BitNav Dir="BitDir.Rtl" + Items="CustomRtlBitPlatformNavMenu" + NameSelectors="@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })" /> </div> </ExamplePreview> </ComponentExampleBox> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor.cs index 4ad14a6e76..a35eedf962 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavCustomDemo.razor.cs @@ -2,7 +2,7 @@ public partial class _BitNavCustomDemo { - private static readonly List<BitPlatformMenu> CustomBitPlatformNavMenu = + private static readonly List<Section> CustomBitPlatformNavMenu = [ new() { @@ -48,6 +48,25 @@ public partial class _BitNavCustomDemo new() { Text = "Iconography", Icon = BitIconName.AppIconDefault, Url = "/iconography" }, ]; + private static readonly List<Section> CustomIconOnlyNavMenu = + [ + new() { Text = "Home", Icon = BitIconName.Home }, + new() { + Text = "AdminPanel sample", + Icon = BitIconName.LocalAdmin, + Links = + [ + new() { Text = "Dashboard", Icon = BitIconName.ViewDashboard }, + new() { Text = "Categories", Icon = BitIconName.BuildQueue }, + new() { Text = "Products", Icon = BitIconName.Product }, + ] + }, + new() { Text = "Todo sample", Icon = BitIconName.ToDoLogoOutline}, + new() { Text = "BlazorUI", Icon = BitIconName.F12DevTools }, + new() { Text = "Bit academy", Icon = BitIconName.LearningTools, IsEnabled = false }, + new() { Text = "Contact us", Icon = BitIconName.Contact }, + ]; + private static readonly List<CarMenu> CustomCarNavMenu = [ new() @@ -158,7 +177,7 @@ public partial class _BitNavCustomDemo new() { Name = "Cookie" }, ]; - private static readonly List<BitPlatformMenu> CustomCustomStyleNavMenu = + private static readonly List<Section> CustomCustomStyleNavMenu = [ new() { @@ -204,7 +223,7 @@ public partial class _BitNavCustomDemo new() { Text = "Iconography", Icon = BitIconName.AppIconDefault, Url = "/iconography" }, ]; - private static readonly List<BitPlatformMenu> CustomRtlBitPlatformNavMenu = + private static readonly List<Section> CustomRtlBitPlatformNavMenu = [ new() { @@ -250,6 +269,8 @@ public partial class _BitNavCustomDemo new() { Text = "شمایل نگاری", Icon = BitIconName.AppIconDefault, Url = "/iconography" }, ]; + private bool iconOnly; + private static List<FoodMenu> Flatten(IList<FoodMenu> e) => e.SelectMany(c => Flatten(c.Childs)).Concat(e).ToList(); private FoodMenu CustomSelectedFood = CustomFoodNavMenu[0].Childs[2]; private string? CustomSelectedFoodName = CustomFoodNavMenu[0].Childs[2].Name; @@ -278,23 +299,23 @@ public partial class _BitNavCustomDemo - private readonly string example1CustomItemRazorCode = @" + private readonly string example1RazorCode = @" <BitNav Items=""CustomBitPlatformNavMenu"" - NameSelectors=""@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })"" />"; - private readonly string example1CustomItemCsharpCode = @" -public class BitPlatformMenu + NameSelectors=""@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })"" />"; + private readonly string example1CsharpCode = @" +public class Section { public string Text { get; set; } = string.Empty; public string? Icon { get; set; } public string? Url { get; set; } public bool IsEnabled { get; set; } = true; public bool IsExpanded { get; set; } - public List<BitPlatformMenu> Links { get; set; } = []; + public List<Section> Links { get; set; } = []; } -private static readonly List<BitPlatformMenu> CustomBitPlatformNavMenu = +private static readonly List<Section> CustomBitPlatformNavMenu = [ new() { @@ -340,7 +361,69 @@ public class BitPlatformMenu new() { Text = ""Iconography"", Icon = BitIconName.AppIconDefault, Url = ""/iconography"" }, ];"; - private readonly string example2CustomItemRazorCode = @" + private readonly string example2RazorCode = @" +<BitNav Items=""CustomBitPlatformNavMenu"" FitWidth + NameSelectors=""@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })"" />"; + private readonly string example2CsharpCode = @" +public class Section +{ + public string Text { get; set; } = string.Empty; + public string? Icon { get; set; } + public string? Url { get; set; } + public bool IsEnabled { get; set; } = true; + public bool IsExpanded { get; set; } + public List<Section> Links { get; set; } = []; +} + +private static readonly List<Section> CustomBitPlatformNavMenu = +[ + new() + { + Text = ""bit platform"", + Comment = ""the bit platform description"", + Links = + [ + new() { Text = ""Home"", Icon = BitIconName.Home, Url = ""https://bitplatform.dev/"" }, + new() + { + Text = ""Products & Services"", + Links = + [ + new() + { + Text = ""Project Templates"", + Links = + [ + new() { Text = ""Todo sample"", Icon = BitIconName.ToDoLogoOutline, Url = ""https://bitplatform.dev/templates/overview"" }, + new() { Text = ""AdminPanel sample"", Icon = BitIconName.LocalAdmin, Url = ""https://bitplatform.dev/templates/overview"" }, + ] + }, + new() { Text = ""BlazorUI"", Icon = BitIconName.F12DevTools, Url = ""https://bitplatform.dev/components"" }, + new() { Text = ""Cloud hosting solutions"", Icon = BitIconName.Cloud, Url = ""https://bitplatform.dev/#"", IsEnabled = false }, + new() { Text = ""Bit academy"", Icon = BitIconName.LearningTools, Url = ""https://bitplatform.dev/#"", IsEnabled = false }, + ] + }, + new() { Text = ""Pricing"", Icon = BitIconName.Money, Url = ""https://bitplatform.dev/pricing"" }, + new() { Text = ""About"", Icon = BitIconName.Info, Url = ""https://bitplatform.dev/about-us"" }, + new() { Text = ""Contact us"", Icon = BitIconName.Contact, Url = ""https://bitplatform.dev/contact-us"" }, + ], + }, + new() + { + Text = ""Community"", + Links = + [ + new() { Text = ""Linkedin"", Icon = BitIconName.LinkedInLogo, Url = ""https://www.linkedin.com/company/bitplatformhq"" }, + new() { Text = ""Twitter"", Icon = BitIconName.Globe, Url = ""https://twitter.com/bitplatformhq"" }, + new() { Text = ""Github repo"", Icon = BitIconName.GitGraph, Url = ""https://github.com/bitfoundation/bitplatform"" }, + ] + }, + new() { Text = ""Iconography"", Icon = BitIconName.AppIconDefault, Url = ""/iconography"" }, +];"; + + private readonly string example3RazorCode = @" <BitNav Items=""CustomCarNavMenu"" RenderType=""BitNavRenderType.Grouped"" NameSelectors=""@(new() { Text = { Name = nameof(CarMenu.Name) }, @@ -352,7 +435,7 @@ public class BitPlatformMenu ExpandAriaLabel = { Name = nameof(CarMenu.ExpandedAriaLabel) }, ChildItems = { Name = nameof(CarMenu.Links) }, Description = { Name = nameof(CarMenu.Comment) } })"" />"; - private readonly string example2CustomItemCsharpCode = @" + private readonly string example3CsharpCode = @" public class CarMenu { public string Name { get; set; } = string.Empty; @@ -424,7 +507,7 @@ public class CarMenu }, ];"; - private readonly string example3CustomItemRazorCode = @" + private readonly string example4RazorCode = @" <BitNav Items=""CustomFoodNavMenu"" Mode=""BitNavMode.Manual"" DefaultSelectedItem=""CustomFoodNavMenu[0].Childs[2]"" @@ -448,7 +531,7 @@ public class CarMenu Label=""Select Item"" Items=""FoodMenuDropdownItems"" OnSelectItem=""(BitDropdownItem<string> item) => CustomSelectedFood = Flatten(CustomFoodNavMenu).Single(i => i.Name == item.Value)"" />"; - private readonly string example3CustomItemCsharpCode = @" + private readonly string example4CsharpCode = @" public class FoodMenu { public string Name { get; set; } = string.Empty; @@ -530,7 +613,45 @@ public class FoodMenu private FoodMenu CustomSelectedFood = CustomFoodNavMenu[0].Childs[2]; private string CustomSelectedFoodName = CustomFoodNavMenu[0].Childs[2].Name;"; - private readonly string example4CustomItemRazorCode = @" + private readonly string example5RazorCode = @" +<BitToggle @bind-Value=""iconOnly"" Label=""Hide texts?"" Inline /> +<BitNav Items=""CustomIconOnlyNavMenu"" Mode=""BitNavMode.Manual"" IconOnly=""iconOnly"" + NameSelectors=""@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })"" />"; + private readonly string example5CsharpCode = @" +private bool iconOnly; + +public class Section +{ + public string Text { get; set; } = string.Empty; + public string? Icon { get; set; } + public string? Url { get; set; } + public bool IsEnabled { get; set; } = true; + public bool IsExpanded { get; set; } + public List<Section> Links { get; set; } = []; +} + +private static readonly List<Section> CustomIconOnlyNavMenu = +[ + new() { Text = ""Home"", Icon = BitIconName.Home }, + new() { + Text = ""AdminPanel sample"", + Icon = BitIconName.LocalAdmin, + Links = + [ + new() { Text = ""Dashboard"", Icon = BitIconName.ViewDashboard }, + new() { Text = ""Categories"", Icon = BitIconName.BuildQueue }, + new() { Text = ""Products"", Icon = BitIconName.Product }, + ] + }, + new() { Text = ""Todo sample"", Icon = BitIconName.ToDoLogoOutline}, + new() { Text = ""BlazorUI"", Icon = BitIconName.F12DevTools }, + new() { Text = ""Bit academy"", Icon = BitIconName.LearningTools, IsEnabled = false }, + new() { Text = ""Contact us"", Icon = BitIconName.Contact }, +];"; + + private readonly string example6RazorCode = @" <style> .nav-custom-header { font-size: 17px; @@ -583,7 +704,7 @@ public class FoodMenu </div> </ItemTemplate> </BitNav>"; - private readonly string example4CustomItemCsharpCode = @" + private readonly string example6CsharpCode = @" public class CarMenu { public string Name { get; set; } = string.Empty; @@ -714,7 +835,7 @@ public class FoodMenu new() { Name = ""Cookie"" }, ];"; - private readonly string example5CustomItemRazorCode = @" + private readonly string example7RazorCode = @" <BitNav Items=""CustomFoodNavMenu"" Mode=""BitNavMode.Manual"" OnItemClick=""(FoodMenu item) => CustomClickedItem = item"" @@ -727,7 +848,7 @@ public class FoodMenu <div>Clicked Item: @CustomClickedItem?.Name</div> <div>Selected Item: @CustomSelectedItem?.Name</div> <div>Toggled Item: @(CustomToggledItem is null ? ""N/A"" : $""{CustomToggledItem.Name} ({(CustomToggledItem.IsExpanded ? ""Expanded"" : ""Collapsed"")})"")</div>"; - private readonly string example5CustomItemCsharpCode = @" + private readonly string example7CsharpCode = @" public class FoodMenu { public string Name { get; set; } = string.Empty; @@ -791,27 +912,27 @@ public class FoodMenu private FoodMenu CustomSelectedItem; private FoodMenu CustomToggledItem;"; - private readonly string example6CustomItemRazorCode = @" + private readonly string example8RazorCode = @" <BitNav Items=""CustomCustomStyleNavMenu"" - NameSelectors=""@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })"" + NameSelectors=""@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })"" Styles=""@(new() { ItemContainer = ""border: 1px solid green; margin: 2px;"", ToggleButton = ""color: cyan;"", Item = ""color: red;"", ItemIcon = ""color: gold; margin-right: 15px;"" })"" />"; - private readonly string example6CustomItemCsharpCode = @" -public class BitPlatformMenu + private readonly string example8CsharpCode = @" +public class Section { public string Text { get; set; } = string.Empty; public string? Icon { get; set; } public string? Url { get; set; } public bool IsEnabled { get; set; } = true; public bool IsExpanded { get; set; } - public List<BitPlatformMenu> Links { get; set; } = []; + public List<Section> Links { get; set; } = []; } -private static readonly List<BitPlatformMenu> CustomCustomStyleNavMenu = +private static readonly List<Section> CustomCustomStyleNavMenu = [ new() { @@ -857,24 +978,24 @@ public class BitPlatformMenu new() { Text = ""Iconography"", Icon = BitIconName.AppIconDefault, Url = ""/iconography"" }, ];"; - private readonly string example7CustomItemRazorCode = @" + private readonly string example9RazorCode = @" <BitNav Dir=""BitDir.Rtl"" Items=""CustomRtlBitPlatformNavMenu"" - NameSelectors=""@(new() { IconName = { Name = nameof(BitPlatformMenu.Icon) }, - ChildItems = { Name = nameof(BitPlatformMenu.Links) }, - Description = { Name = nameof(BitPlatformMenu.Comment) } })"" />"; - private readonly string example7CustomItemCsharpCode = @" -public class BitPlatformMenu + NameSelectors=""@(new() { IconName = { Name = nameof(Section.Icon) }, + ChildItems = { Name = nameof(Section.Links) }, + Description = { Name = nameof(Section.Comment) } })"" />"; + private readonly string example9CsharpCode = @" +public class Section { public string Text { get; set; } = string.Empty; public string? Icon { get; set; } public string? Url { get; set; } public bool IsEnabled { get; set; } = true; public bool IsExpanded { get; set; } - public List<BitPlatformMenu> Links { get; set; } = []; + public List<Section> Links { get; set; } = []; } -private static readonly List<BitPlatformMenu> CustomRtlBitPlatformNavMenu = +private static readonly List<Section> CustomRtlBitPlatformNavMenu = [ new() { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor index ce341a2b11..723eead5d7 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor @@ -1,121 +1,109 @@ -<ComponentExampleBox Title="Basic" RazorCode="@example1NavItemRazorCode" CsharpCode="@example1NavItemCsharpCode" Id="example1"> +<ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="BitPlatformNavMenu" /> - </div> + <BitNav Items="BitPlatformNavMenu" /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Grouped" RazorCode="@example2NavItemRazorCode" CsharpCode="@example2NavItemCsharpCode" Id="example2"> +<ComponentExampleBox Title="FitWidth" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="CarNavMenu" RenderType="BitNavRenderType.Grouped" /> - </div> + <BitNav Items="BitPlatformNavMenu" FitWidth /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Manual Mode" RazorCode="@example3NavItemRazorCode" CsharpCode="@example3NavItemCsharpCode" Id="example3"> +<ComponentExampleBox Title="Grouped" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div class="example-box"> - <div> - <BitLabel>Basic</BitLabel> - <BitNav Items="FoodNavMenu" - DefaultSelectedItem="FoodNavMenu[0].ChildItems[2]" - Mode="BitNavMode.Manual" /> - </div> + <BitNav Items="CarNavMenu" RenderType="BitNavRenderType.Grouped" /> + </ExamplePreview> +</ComponentExampleBox> +<ComponentExampleBox Title="Manual Mode" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ExamplePreview> + <div>Basic</div><br /> + <BitNav Items="FoodNavMenu" + DefaultSelectedItem="FoodNavMenu[0].ChildItems[2]" + Mode="BitNavMode.Manual" /> + <br /><br /><br /> + <div>Two-Way Bind</div><br /> + <div class="example-box"> + <BitNav @bind-SelectedItem="SelectedItemNav" + Items="FoodNavMenu" + Mode="BitNavMode.Manual" + OnSelectItem="(BitNavItem item) => SelectedItemText = FoodMenuDropdownItems.FirstOrDefault(i => i.Text == item.Text)?.Text" /> <br /> - <br /> - <br /> - - <div class="margin-top"> - <BitLabel>Two-Way Bind</BitLabel> - - <BitNav @bind-SelectedItem="SelectedItemNav" - Items="FoodNavMenu" - Mode="BitNavMode.Manual" - OnSelectItem="(BitNavItem item) => SelectedItemText = FoodMenuDropdownItems.FirstOrDefault(i => i.Text == item.Text)?.Text" /> - - <BitDropdown @bind-Value="SelectedItemText" - Label="Select Item" - Items="FoodMenuDropdownItems" - OnSelectItem="(BitDropdownItem<string> item) => SelectedItemNav = Flatten(FoodNavMenu).First(i => i.Text == item.Value)" /> - </div> + <BitDropdown @bind-Value="SelectedItemText" + FitWidth + Label="Select Item" + Items="FoodMenuDropdownItems" + OnSelectItem="(BitDropdownItem<string> item) => SelectedItemNav = Flatten(FoodNavMenu).First(i => i.Text == item.Value)" /> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Templates" RazorCode="@example4NavItemRazorCode" CsharpCode="@example4NavItemCsharpCode" Id="example4"> +<ComponentExampleBox Title="IconOnly" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-box"> - <div> - <BitLabel>Header Template (Grouped)</BitLabel> - <BitNav Items="CarNavMenu" RenderType="BitNavRenderType.Grouped"> - <HeaderTemplate Context="item"> - <div class="nav-custom-header"> - <BitIcon IconName="@BitIconName.FavoriteStarFill" /> - <span>@item.Text</span> - </div> - </HeaderTemplate> - </BitNav> - </div> - - <br /> - <br /> - <br /> + <BitToggle @bind-Value="iconOnly" Label="Hide texts?" Inline /> + <br /> + <BitNav Items="IconOnlyNavMenu" Mode="BitNavMode.Manual" IconOnly="iconOnly" /> + </ExamplePreview> +</ComponentExampleBox> - <div class="margin-top"> - <BitLabel>Item Template</BitLabel> - <BitNav Items="FoodNavMenu" Mode="BitNavMode.Manual"> - <ItemTemplate Context="item"> - <div class="nav-custom-item"> - <BitCheckbox /> - <BitIcon IconName="@item.IconName" /> - <span>@item.Text</span> - </div> - </ItemTemplate> - </BitNav> - </div> - </div> +<ComponentExampleBox Title="Custom Templates" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ExamplePreview> + <div>Header Template (Grouped)</div><br /> + <BitNav Items="CarNavMenu" RenderType="BitNavRenderType.Grouped"> + <HeaderTemplate Context="item"> + <div class="nav-custom-header"> + <BitIcon IconName="@BitIconName.FavoriteStarFill" /> + <span>@item.Text</span> + </div> + </HeaderTemplate> + </BitNav> + <br /><br /><br /> + <div>Item Template</div><br /> + <BitNav Items="FoodNavMenu" Mode="BitNavMode.Manual"> + <ItemTemplate Context="item"> + <div class="nav-custom-item"> + <BitCheckbox /> + <BitIcon IconName="@item.IconName" /> + <span>@item.Text</span> + </div> + </ItemTemplate> + </BitNav> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Events" RazorCode="@example5NavItemRazorCode" CsharpCode="@example5NavItemCsharpCode" Id="example5"> +<ComponentExampleBox Title="Events" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <div class="events-example-box"> + <div class="example-box"> <BitNav Items="FoodNavMenu" Mode="BitNavMode.Manual" OnItemClick="(BitNavItem item) => ClickedItem = item" OnSelectItem="(BitNavItem item) => SelectedItem = item" OnItemToggle="(BitNavItem item) => ToggledItem = item" /> - <div class="flex"> - <span>Clicked Item: <b>@ClickedItem?.Text</b></span> - <span>Selected Item: <b>@SelectedItem?.Text</b></span> + <div> + <span>Clicked Item: <b>@ClickedItem?.Text</b></span><br /> + <span>Selected Item: <b>@SelectedItem?.Text</b></span><br /> <span>Toggled Item: <b>@(ToggledItem is null ? "N/A" : $"{ToggledItem.Text} ({(ToggledItem.IsExpanded ? "Expanded" : "Collapsed")})")</b></span> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Styles" RazorCode="@example6NavItemRazorCode" CsharpCode="@example6NavItemCsharpCode" Id="example6"> +<ComponentExampleBox Title="Custom Styles" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> - <div class="example-box"> - <BitNav Items="CustomStyleNavMenu" - Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", - ToggleButton = "color: cyan;", - Item = "color: red;", - ItemIcon = "color: gold; margin-right: 15px;" })" /> - </div> + <BitNav Items="CustomStyleNavMenu" + Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", + ToggleButton = "color: cyan;", + Item = "color: red;", + ItemIcon = "color: gold; margin-right: 15px;" })" /> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7NavItemRazorCode" CsharpCode="@example7NavItemCsharpCode" Id="example7"> +<ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> <ExamplePreview> <div dir="rtl"> - <div class="example-box"> - <BitNav Dir="BitDir.Rtl" Items="RtlBitPlatformNavMenu" /> - </div> + <BitNav Dir="BitDir.Rtl" Items="RtlBitPlatformNavMenu" /> </div> </ExamplePreview> </ComponentExampleBox> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor.cs index 0531e82486..3c543dc503 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavItemDemo.razor.cs @@ -48,6 +48,25 @@ public partial class _BitNavItemDemo new() { Text = "Iconography", IconName = BitIconName.AppIconDefault, Url = "/iconography" }, ]; + private static readonly List<BitNavItem> IconOnlyNavMenu = + [ + new() { Text = "Home", IconName = BitIconName.Home }, + new() { + Text = "AdminPanel sample", + IconName = BitIconName.LocalAdmin, + ChildItems = + [ + new() { Text = "Dashboard", IconName = BitIconName.ViewDashboard }, + new() { Text = "Categories", IconName = BitIconName.BuildQueue }, + new() { Text = "Products", IconName = BitIconName.Product }, + ] + }, + new() { Text = "Todo sample", IconName = BitIconName.ToDoLogoOutline}, + new() { Text = "BlazorUI", IconName = BitIconName.F12DevTools }, + new() { Text = "Bit academy", IconName = BitIconName.LearningTools, IsEnabled = false }, + new() { Text = "Contact us", IconName = BitIconName.Contact }, + ]; + private static readonly List<BitNavItem> CarNavMenu = [ new() @@ -250,6 +269,8 @@ public partial class _BitNavItemDemo new() { Text = "شمایل نگاری", IconName = BitIconName.AppIconDefault, Url = "/iconography" }, ]; + private bool iconOnly; + private static List<BitNavItem> Flatten(IList<BitNavItem> e) => e.SelectMany(c => Flatten(c.ChildItems)).Concat(e).ToList(); private BitNavItem SelectedItemNav = FoodNavMenu[0].ChildItems[2]; private string? SelectedItemText = FoodNavMenu[0].ChildItems[2].Text; @@ -278,9 +299,58 @@ public partial class _BitNavItemDemo - private readonly string example1NavItemRazorCode = @" + private readonly string example1RazorCode = @" <BitNav Items=""BitPlatformNavMenu"" />"; - private readonly string example1NavItemCsharpCode = @" + private readonly string example1CsharpCode = @" +private static readonly List<BitNavItem> BitPlatformNavMenu = +[ + new() + { + Text = ""bit platform"", + Description = ""the bit platform description"", + ChildItems = + [ + new() { Text = ""Home"", IconName = BitIconName.Home, Url = ""https://bitplatform.dev/"" }, + new() + { + Text = ""Products & Services"", + ChildItems = + [ + new() + { + Text = ""Project Templates"", + ChildItems = + [ + new() { Text = ""Todo sample"", IconName = BitIconName.ToDoLogoOutline, Url = ""https://bitplatform.dev/templates/overview"" }, + new() { Text = ""AdminPanel sample"", IconName = BitIconName.LocalAdmin, Url = ""https://bitplatform.dev/templates/overview"" }, + ] + }, + new() { Text = ""BlazorUI"", IconName = BitIconName.F12DevTools, Url = ""https://bitplatform.dev/components"" }, + new() { Text = ""Cloud hosting solutions"", IconName = BitIconName.Cloud, Url = ""https://bitplatform.dev/#"", IsEnabled = false }, + new() { Text = ""Bit academy"", IconName = BitIconName.LearningTools, Url = ""https://bitplatform.dev/#"", IsEnabled = false }, + ] + }, + new() { Text = ""Pricing"", IconName = BitIconName.Money, Url = ""https://bitplatform.dev/pricing"" }, + new() { Text = ""About"", IconName = BitIconName.Info, Url = ""https://bitplatform.dev/about-us"" }, + new() { Text = ""Contact us"", IconName = BitIconName.Contact, Url = ""https://bitplatform.dev/contact-us"" }, + ], + }, + new() + { + Text = ""Community"", + ChildItems = + [ + new() { Text = ""LinkedIn"", IconName = BitIconName.LinkedInLogo , Url = ""https://www.linkedin.com/company/bitplatformhq"" }, + new() { Text = ""Twitter"", IconName = BitIconName.Globe , Url = ""https://twitter.com/bitplatformhq"" }, + new() { Text = ""GitHub repo"", IconName = BitIconName.GitGraph , Url = ""https://github.com/bitfoundation/bitplatform"" }, + ] + }, + new() { Text = ""Iconography"", IconName = BitIconName.AppIconDefault, Url = ""/iconography"" }, +];"; + + private readonly string example2RazorCode = @" +<BitNav Items=""BitPlatformNavMenu"" FitWidth />"; + private readonly string example2CsharpCode = @" private static readonly List<BitNavItem> BitPlatformNavMenu = [ new() @@ -327,9 +397,9 @@ public partial class _BitNavItemDemo new() { Text = ""Iconography"", IconName = BitIconName.AppIconDefault, Url = ""/iconography"" }, ];"; - private readonly string example2NavItemRazorCode = @" + private readonly string example3RazorCode = @" <BitNav Items=""CarNavMenu"" RenderType=""BitNavRenderType.Grouped"" />"; - private readonly string example2NavItemCsharpCode = @" + private readonly string example3CsharpCode = @" private static readonly List<BitNavItem> CarNavMenu = [ new() @@ -389,13 +459,13 @@ public partial class _BitNavItemDemo }, ];"; - private readonly string example3NavItemRazorCode = @" -<BitLabel>Basic</BitLabel> + private readonly string example4RazorCode = @" +<div>Basic</div> <BitNav Items=""FoodNavMenu"" DefaultSelectedItem=""FoodNavMenu[0].Items[2]"" Mode=""BitNavMode.Manual"" /> -<BitLabel>Two-Way Bind</BitLabel> +<div>Two-Way Bind</div> <BitNav @bind-SelectedItem=""SelectedItemNav"" Items=""FoodNavMenu"" Mode=""BitNavMode.Manual"" @@ -405,7 +475,7 @@ public partial class _BitNavItemDemo Label=""Select Item"" Items=""FoodMenuDropdownItems"" OnSelectItem=""(item) => SelectedItemNav = Flatten(FoodNavMenu).FirstOrDefault(i => i.Text == item.Value)"" />"; - private readonly string example3NavItemCsharpCode = @" + private readonly string example4CsharpCode = @" private static readonly List<BitNavItem> FoodNavMenu = [ new() @@ -535,7 +605,32 @@ public partial class _BitNavItemDemo private BitNavItem SelectedItemNav = FoodNavMenu[0].Items[2]; private string SelectedItemText = FoodNavMenu[0].Items[2].Text;"; - private readonly string example4NavItemRazorCode = @" + private readonly string example5RazorCode = @" +<BitToggle @bind-Value=""iconOnly"" Label=""Hide texts?"" Inline /> +<BitNav Items=""IconOnlyNavMenu"" Mode=""BitNavMode.Manual"" IconOnly=""iconOnly"" />"; + private readonly string example5CsharpCode = @" +private bool iconOnly; + +private static readonly List<BitNavItem> IconOnlyNavMenu = +[ + new() { Text = ""Home"", IconName = BitIconName.Home }, + new() { + Text = ""AdminPanel sample"", + IconName = BitIconName.LocalAdmin, + ChildItems = + [ + new() { Text = ""Dashboard"", IconName = BitIconName.ViewDashboard }, + new() { Text = ""Categories"", IconName = BitIconName.BuildQueue }, + new() { Text = ""Products"", IconName = BitIconName.Product }, + ] + }, + new() { Text = ""Todo sample"", IconName = BitIconName.ToDoLogoOutline}, + new() { Text = ""BlazorUI"", IconName = BitIconName.F12DevTools }, + new() { Text = ""Bit academy"", IconName = BitIconName.LearningTools, IsEnabled = false }, + new() { Text = ""Contact us"", IconName = BitIconName.Contact }, +];"; + + private readonly string example6RazorCode = @" <style> .nav-custom-header { font-size: 17px; @@ -553,7 +648,7 @@ public partial class _BitNavItemDemo } </style> -<BitLabel>Header Template (in Grouped mode)</BitLabel> +<div>Header Template (in Grouped mode)</div> <BitNav Items=""CarNavMenu"" RenderType=""BitNavRenderType.Grouped""> <HeaderTemplate Context=""item""> <div class=""nav-custom-header""> @@ -563,7 +658,7 @@ public partial class _BitNavItemDemo </HeaderTemplate> </BitNav> -<BitLabel>Item Template</BitLabel> +<div>Item Template</div> <BitNav Items=""FoodNavMenu"" Mode=""BitNavMode.Manual""> <ItemTemplate Context=""item""> <div class=""nav-custom-item""> @@ -573,7 +668,7 @@ public partial class _BitNavItemDemo </div> </ItemTemplate> </BitNav>"; - private readonly string example4NavItemCsharpCode = @" + private readonly string example6CsharpCode = @" private static readonly List<BitNavItem> CarNavMenu = [ new() @@ -684,7 +779,7 @@ public partial class _BitNavItemDemo new() { Text = ""Cookie"" }, ];"; - private readonly string example5NavItemRazorCode = @" + private readonly string example7RazorCode = @" <BitNav Items=""FoodNavMenu"" Mode=""BitNavMode.Manual"" OnItemClick=""(BitNavItem item) => ClickedItem = item"" @@ -694,7 +789,7 @@ public partial class _BitNavItemDemo <span>Clicked Item: @ClickedItem?.Text</span> <span>Selected Item: @SelectedItem?.Text</span> <span>Toggled Item: @(ToggledItem is null ? ""N/A"" : $""{ToggledItem.Text} ({(ToggledItem.IsExpanded ? ""Expanded"" : ""Collapsed"")})"")</span>"; - private readonly string example5NavItemCsharpCode = @" + private readonly string example7CsharpCode = @" private static readonly List<BitNavItem> FoodNavMenu = [ new() @@ -750,13 +845,13 @@ public partial class _BitNavItemDemo private BitNavItem SelectedItem; private BitNavItem ToggledItem;"; - private readonly string example6NavItemRazorCode = @" + private readonly string example8RazorCode = @" <BitNav Items=""CustomStyleNavMenu"" Styles=""@(new() { ItemContainer = ""border: 1px solid green; margin: 2px;"", ToggleButton = ""color: cyan;"", Item = ""color: red;"", ItemIcon = ""color: gold; margin-right: 15px;"" })"" />"; - private readonly string example6NavItemCsharpCode = @" + private readonly string example8CsharpCode = @" private static readonly List<BitNavItem> CustomStyleNavMenu = [ new() @@ -803,9 +898,9 @@ public partial class _BitNavItemDemo new() { Text = ""Iconography"", IconName = BitIconName.AppIconDefault, Url = ""/iconography"" }, ];"; - private readonly string example7NavItemRazorCode = @" + private readonly string example9RazorCode = @" <BitNav Dir=""BitDir.Rtl"" Items=""RtlBitPlatformNavMenu"" />"; - private readonly string example7NavItemCsharpCode = @" + private readonly string example9CsharpCode = @" private static readonly List<BitNavItem> RtlBitPlatformNavMenu = [ new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor index 6f22ecd690..6178b64b7d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor @@ -1,45 +1,205 @@ -<ComponentExampleBox Title="Basic" RazorCode="@example1NavOptionRazorCode" Id="example1"> +<ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitNav TItem="BitNavOption"> - <BitNavOption Text="bit platform" - Description="the bit platform description" - ExpandAriaLabel="bit platform Expanded" - CollapseAriaLabel="bit platform Collapsed"> - <BitNavOption Text="Home" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> - <BitNavOption Text="Products & Services"> - <BitNavOption Text="Project Templates"> - <BitNavOption IconName="@BitIconName.ToDoLogoOutline" Text="Todo sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - <BitNavOption IconName="@BitIconName.LocalAdmin" Text="AdminPanel sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" Url="https://bitplatform.dev/components" Target="_blank" /> - <BitNavOption Text="Cloud hosting solutions" IconName="@BitIconName.Cloud" IsEnabled="false" /> - <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> + <BitNav TItem="BitNavOption"> + <BitNavOption Text="bit platform" + Description="the bit platform description" + ExpandAriaLabel="bit platform Expanded" + CollapseAriaLabel="bit platform Collapsed"> + <BitNavOption Text="Home" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> + <BitNavOption Text="Products & Services"> + <BitNavOption Text="Project Templates"> + <BitNavOption Text="Todo sample" IconName="@BitIconName.ToDoLogoOutline" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> + <BitNavOption Text="AdminPanel sample" IconName="@BitIconName.LocalAdmin" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> </BitNavOption> - <BitNavOption Text="Pricing" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> - <BitNavOption Text="About" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> - <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" Url="https://bitplatform.dev/components" Target="_blank" /> + <BitNavOption Text="Cloud hosting solutions" IconName="@BitIconName.Cloud" IsEnabled="false" /> + <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> </BitNavOption> - <BitNavOption Text="Community" - ExpandAriaLabel="Community Expanded" - CollapseAriaLabel="Community Collapsed"> - <BitNavOption Text="LinkedIn" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> - <BitNavOption Text="Twitter" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> - <BitNavOption Text="Github repo" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + <BitNavOption Text="Pricing" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> + <BitNavOption Text="About" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> + <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Community" + ExpandAriaLabel="Community Expanded" + CollapseAriaLabel="Community Collapsed"> + <BitNavOption Text="LinkedIn" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> + <BitNavOption Text="Twitter" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> + <BitNavOption Text="Github repo" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + </BitNavOption> + + <BitNavOption Text="Iconography" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> + </BitNav> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="FitWidth" RazorCode="@example2RazorCode" Id="example2"> + <ExamplePreview> + <BitNav TItem="BitNavOption" FitWidth> + <BitNavOption Text="bit platform" + Description="the bit platform description" + ExpandAriaLabel="bit platform Expanded" + CollapseAriaLabel="bit platform Collapsed"> + <BitNavOption Text="Home" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> + <BitNavOption Text="Products & Services"> + <BitNavOption Text="Project Templates"> + <BitNavOption Text="Todo sample" IconName="@BitIconName.ToDoLogoOutline" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> + <BitNavOption Text="AdminPanel sample" IconName="@BitIconName.LocalAdmin" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" Url="https://bitplatform.dev/components" Target="_blank" /> + <BitNavOption Text="Cloud hosting solutions" IconName="@BitIconName.Cloud" IsEnabled="false" /> + <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> </BitNavOption> + <BitNavOption Text="Pricing" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> + <BitNavOption Text="About" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> + <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Community" + ExpandAriaLabel="Community Expanded" + CollapseAriaLabel="Community Collapsed"> + <BitNavOption Text="LinkedIn" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> + <BitNavOption Text="Twitter" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> + <BitNavOption Text="Github repo" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + </BitNavOption> - <BitNavOption Text="Iconography" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> + <BitNavOption Text="Iconography" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> + </BitNav> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="Grouped" RazorCode="@example3RazorCode" Id="example3"> + <ExamplePreview> + <BitNav TItem="BitNavOption" RenderType="BitNavRenderType.Grouped"> + <BitNavOption Text="Mercedes-Benz" + Description="Cars manufactured under the brand of Mercedes-Benz" + ExpandAriaLabel="Mercedes-Benz Expanded" + CollapseAriaLabel="Mercedes-Benz Collapsed" + Title="Mercedes-Benz Car Models" + IsExpanded="true"> + <BitNavOption Text="SUVs"> + <BitNavOption Text="GLA" Url="https://www.mbusa.com/en/vehicles/class/gla/suv" Target="_blank" /> + <BitNavOption Text="GLB" Url="https://www.mbusa.com/en/vehicles/class/glb/suv" Target="_blank" /> + <BitNavOption Text="GLC" Url="https://www.mbusa.com/en/vehicles/class/glc/suv" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Sedans & Wagons"> + <BitNavOption Text="A Class" Url="https://www.mbusa.com/en/vehicles/class/a-class/sedan" Target="_blank" /> + <BitNavOption Text="C Class" Url="https://www.mbusa.com/en/vehicles/class/c-class/sedan" Target="_blank" /> + <BitNavOption Text="E Class" Url="https://www.mbusa.com/en/vehicles/class/e-class/sedan" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Coupes"> + <BitNavOption Text="CLA Coupe" Url="https://www.mbusa.com/en/vehicles/class/cla/coupe" Target="_blank" /> + <BitNavOption Text="C Class Coupe" Url="https://www.mbusa.com/en/vehicles/class/c-class/coupe" Target="_blank" /> + <BitNavOption Text="E Class Coupe" Url="https://www.mbusa.com/en/vehicles/class/e-class/coupe" Target="_blank" /> + </BitNavOption> + </BitNavOption> + <BitNavOption Text="Tesla" + ExpandAriaLabel="Tesla Expanded" + CollapseAriaLabel="Tesla Collapsed" + Title="Tesla Car Models"> + <BitNavOption Text="Model S" Url="https://www.tesla.com/models" Target="_blank" /> + <BitNavOption Text="Model X" Url="https://www.tesla.com/modelx" Target="_blank" /> + <BitNavOption Text="Model Y" Url="https://www.tesla.com/modely" Target="_blank" /> + </BitNavOption> + </BitNav> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="Manual Mode" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ExamplePreview> + <div>Basic</div><br /> + <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual"> + <BitNavOption Text="Fast foods" Description="List of fast foods" + IconName="@BitIconName.HeartBroken" IsExpanded="true"> + <BitNavOption Text="Burgers" Description="List of burgers"> + <BitNavOption Text="Beef Burger" Key="Beef Burger" /> + <BitNavOption Text="Veggie Burger" Key="Veggie Burger" /> + <BitNavOption Text="Bison Burger" Key="Bison Burger" /> + <BitNavOption Text="Wild Salmon Burger" Key="Wild Salmon Burger" /> + </BitNavOption> + <BitNavOption Text="Pizza"> + <BitNavOption Text="Cheese Pizza" Key="Cheese Pizza" /> + <BitNavOption Text="Veggie Pizza" Key="Veggie Pizza" /> + <BitNavOption Text="Pepperoni Pizza" Key="Pepperoni Pizza" /> + <BitNavOption Text="Meat Pizza" Key="Meat Pizza" /> + </BitNavOption> + <BitNavOption Text="French Fries" Key="French Fries" /> + </BitNavOption> + <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> + <BitNavOption Text="Apple" Key="Apple" /> + <BitNavOption Text="Orange" Key="Orange" /> + <BitNavOption Text="Benana" Key="Benana" /> + </BitNavOption> + <BitNavOption Text="Ice Cream" Key="Ice Cream" /> + <BitNavOption Text="Cookie" Key="Cookie" /> + </BitNav> + <br /><br /><br /> + <div>Two-Way Bind</div><br /> + <div class="example-box"> + <BitNav Mode="BitNavMode.Manual" + OnSelectItem="(BitNavOption option) => SelectedOptionKey = option.Key"> + <BitNavOption Text="Fast foods" Description="List of fast foods" + IconName="@BitIconName.HeartBroken" IsExpanded="true"> + <BitNavOption Text="Burgers" Description="List of burgers"> + <BitNavOption Text="Beef Burger" Key="Beef Burger" /> + <BitNavOption Text="Veggie Burger" Key="Veggie Burger" /> + <BitNavOption Text="Bison Burger" Key="Bison Burger" /> + <BitNavOption Text="Wild Salmon Burger" Key="Wild Salmon Burger" /> + </BitNavOption> + <BitNavOption Text="Pizza"> + <BitNavOption Text="Cheese Pizza" Key="Cheese Pizza" /> + <BitNavOption Text="Veggie Pizza" Key="Veggie Pizza" /> + <BitNavOption Text="Pepperoni Pizza" Key="Pepperoni Pizza" /> + <BitNavOption Text="Meat Pizza" Key="Meat Pizza" /> + </BitNavOption> + <BitNavOption Text="French Fries" Key="French Fries" /> + </BitNavOption> + <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> + <BitNavOption Text="Aplle" Key="Aplle" /> + <BitNavOption Text="Orange" Key="Orange" /> + <BitNavOption Text="Benana" Key="Benana" /> + </BitNavOption> + <BitNavOption Text="Ice Cream" Key="Ice Cream" /> + <BitNavOption Text="Cookie" Key="Cookie" /> </BitNav> + <br /> + <BitDropdown @bind-Value="SelectedOptionKey" + FitWidth + Label="Select Item" + Items="FoodMenuDropdownItems" /> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Grouped" RazorCode="@example2NavOptionRazorCode" Id="example2"> +<ComponentExampleBox Title="IconOnly" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-box"> - <BitNav TItem="BitNavOption" RenderType="BitNavRenderType.Grouped"> + <BitToggle @bind-Value="iconOnly" Label="Hide texts?" Inline /> + <br /> + <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual" IconOnly="iconOnly"> + <BitNavOption Text="Home" IconName="@BitIconName.Home" /> + <BitNavOption Text="AdminPanel sample" IconName="@BitIconName.LocalAdmin"> + <BitNavOption Text="Dashboard" IconName="@BitIconName.ViewDashboard" /> + <BitNavOption Text="Categories" IconName="@BitIconName.BuildQueue" /> + <BitNavOption Text="Products" IconName="@BitIconName.Product" /> + </BitNavOption> + <BitNavOption Text="Todo sample" IconName="@BitIconName.ToDoLogoOutline" /> + <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" /> + <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> + <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" /> + </BitNav> + </ExamplePreview> +</ComponentExampleBox> + +<ComponentExampleBox Title="Custom Templates" RazorCode="@example6RazorCode" Id="example6"> + <ExamplePreview> + <div>Header Template (Grouped)</div><br /> + <BitNav TItem="BitNavOption" RenderType="BitNavRenderType.Grouped"> + <HeaderTemplate Context="item"> + <div class="nav-custom-header"> + <BitIcon IconName="@BitIconName.FavoriteStarFill" /> + <span>@item.Text</span> + </div> + </HeaderTemplate> + <ChildContent> <BitNavOption Text="Mercedes-Benz" - Description="Cars manufactured under the brand of Mercedes-Benz" ExpandAriaLabel="Mercedes-Benz Expanded" CollapseAriaLabel="Mercedes-Benz Collapsed" Title="Mercedes-Benz Car Models" @@ -68,178 +228,50 @@ <BitNavOption Text="Model X" Url="https://www.tesla.com/modelx" Target="_blank" /> <BitNavOption Text="Model Y" Url="https://www.tesla.com/modely" Target="_blank" /> </BitNavOption> - </BitNav> - </div> - </ExamplePreview> -</ComponentExampleBox> - -<ComponentExampleBox Title="Manual Mode" RazorCode="@example3NavOptionRazorCode" CsharpCode="@example3NavOptionCsharpCode" Id="example3"> - <ExamplePreview> - <div class="example-box"> - <div> - <BitLabel>Basic</BitLabel> - <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual"> - <BitNavOption Text="Fast foods" Description="List of fast foods" - IconName="@BitIconName.HeartBroken" IsExpanded="true"> - <BitNavOption Text="Burgers" Description="List of burgers"> - <BitNavOption Text="Beef Burger" Key="Beef Burger" /> - <BitNavOption Text="Veggie Burger" Key="Veggie Burger" /> - <BitNavOption Text="Bison Burger" Key="Bison Burger" /> - <BitNavOption Text="Wild Salmon Burger" Key="Wild Salmon Burger" /> - </BitNavOption> - <BitNavOption Text="Pizza"> - <BitNavOption Text="Cheese Pizza" Key="Cheese Pizza" /> - <BitNavOption Text="Veggie Pizza" Key="Veggie Pizza" /> - <BitNavOption Text="Pepperoni Pizza" Key="Pepperoni Pizza" /> - <BitNavOption Text="Meat Pizza" Key="Meat Pizza" /> - </BitNavOption> - <BitNavOption Text="French Fries" Key="French Fries" /> - </BitNavOption> - <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> - <BitNavOption Text="Apple" Key="Apple" /> - <BitNavOption Text="Orange" Key="Orange" /> - <BitNavOption Text="Benana" Key="Benana" /> - </BitNavOption> - <BitNavOption Text="Ice Cream" Key="Ice Cream" /> - <BitNavOption Text="Cookie" Key="Cookie" /> - </BitNav> - </div> - - <br /> - <br /> - <br /> - - <div class="margin-top"> - <BitLabel>Two-Way Bind</BitLabel> - <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual"> - <BitNavOption Text="Fast foods" Description="List of fast foods" - IconName="@BitIconName.HeartBroken" IsExpanded="true"> - <BitNavOption Text="Burgers" Description="List of burgers"> - <BitNavOption Text="Beef Burger" Key="Beef Burger" /> - <BitNavOption Text="Veggie Burger" Key="Veggie Burger" /> - <BitNavOption Text="Bison Burger" Key="Bison Burger" /> - <BitNavOption Text="Wild Salmon Burger" Key="Wild Salmon Burger" /> - </BitNavOption> - <BitNavOption Text="Pizza"> - <BitNavOption Text="Cheese Pizza" Key="Cheese Pizza" /> - <BitNavOption Text="Veggie Pizza" Key="Veggie Pizza" /> - <BitNavOption Text="Pepperoni Pizza" Key="Pepperoni Pizza" /> - <BitNavOption Text="Meat Pizza" Key="Meat Pizza" /> - </BitNavOption> - <BitNavOption Text="French Fries" Key="French Fries" /> + </ChildContent> + </BitNav> + <br /><br /><br /> + <div>Item Template</div><br /> + <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual"> + <ItemTemplate Context="option"> + <div class="nav-custom-item"> + <BitCheckbox /> + <BitIcon IconName="@option.IconName" /> + <span>@option.Text</span> + </div> + </ItemTemplate> + <ChildContent> + <BitNavOption Text="Fast foods" Description="List of fast foods" + IconName="@BitIconName.HeartBroken" IsExpanded="true"> + <BitNavOption Text="Burgers" Description="List of burgers"> + <BitNavOption Text="Beef Burger" /> + <BitNavOption Text="Veggie Burger" /> + <BitNavOption Text="Bison Burger" /> + <BitNavOption Text="Wild Salmon Burger" /> </BitNavOption> - <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> - <BitNavOption Text="Aplle" Key="Aplle" /> - <BitNavOption Text="Orange" Key="Orange" /> - <BitNavOption Text="Benana" Key="Benana" /> + <BitNavOption Text="Pizza"> + <BitNavOption Text="Cheese Pizza" /> + <BitNavOption Text="Veggie Pizza" /> + <BitNavOption Text="Pepperoni Pizza" /> + <BitNavOption Text="Meat Pizza" /> </BitNavOption> - <BitNavOption Text="Ice Cream" Key="Ice Cream" /> - <BitNavOption Text="Cookie" Key="Cookie" /> - </BitNav> - - <BitDropdown @bind-Value="SelectedOptionKey" - DefaultValue="@("French Fries")" - Label="Pick one" - Items="FoodMenuDropdownItems" /> - </div> - </div> + <BitNavOption Text="French Fries" /> + </BitNavOption> + <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> + <BitNavOption Text="Aplle" /> + <BitNavOption Text="Orange" /> + <BitNavOption Text="Benana" /> + </BitNavOption> + <BitNavOption Text="Ice Cream" /> + <BitNavOption Text="Cookie" /> + </ChildContent> + </BitNav> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Templates" RazorCode="@example4NavOptionRazorCode" Id="example4"> +<ComponentExampleBox Title="Events" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> <div class="example-box"> - <div> - <BitLabel>Header Template (Grouped)</BitLabel> - <BitNav TItem="BitNavOption" RenderType="BitNavRenderType.Grouped"> - <HeaderTemplate Context="item"> - <div class="nav-custom-header"> - <BitIcon IconName="@BitIconName.FavoriteStarFill" /> - <span>@item.Text</span> - </div> - </HeaderTemplate> - <ChildContent> - <BitNavOption Text="Mercedes-Benz" - ExpandAriaLabel="Mercedes-Benz Expanded" - CollapseAriaLabel="Mercedes-Benz Collapsed" - Title="Mercedes-Benz Car Models" - IsExpanded="true"> - <BitNavOption Text="SUVs"> - <BitNavOption Text="GLA" Url="https://www.mbusa.com/en/vehicles/class/gla/suv" Target="_blank" /> - <BitNavOption Text="GLB" Url="https://www.mbusa.com/en/vehicles/class/glb/suv" Target="_blank" /> - <BitNavOption Text="GLC" Url="https://www.mbusa.com/en/vehicles/class/glc/suv" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="Sedans & Wagons"> - <BitNavOption Text="A Class" Url="https://www.mbusa.com/en/vehicles/class/a-class/sedan" Target="_blank" /> - <BitNavOption Text="C Class" Url="https://www.mbusa.com/en/vehicles/class/c-class/sedan" Target="_blank" /> - <BitNavOption Text="E Class" Url="https://www.mbusa.com/en/vehicles/class/e-class/sedan" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="Coupes"> - <BitNavOption Text="CLA Coupe" Url="https://www.mbusa.com/en/vehicles/class/cla/coupe" Target="_blank" /> - <BitNavOption Text="C Class Coupe" Url="https://www.mbusa.com/en/vehicles/class/c-class/coupe" Target="_blank" /> - <BitNavOption Text="E Class Coupe" Url="https://www.mbusa.com/en/vehicles/class/e-class/coupe" Target="_blank" /> - </BitNavOption> - </BitNavOption> - <BitNavOption Text="Tesla" - ExpandAriaLabel="Tesla Expanded" - CollapseAriaLabel="Tesla Collapsed" - Title="Tesla Car Models"> - <BitNavOption Text="Model S" Url="https://www.tesla.com/models" Target="_blank" /> - <BitNavOption Text="Model X" Url="https://www.tesla.com/modelx" Target="_blank" /> - <BitNavOption Text="Model Y" Url="https://www.tesla.com/modely" Target="_blank" /> - </BitNavOption> - </ChildContent> - </BitNav> - </div> - - <br /> - <br /> - <br /> - - <div class="margin-top"> - <BitLabel>Item Template</BitLabel> - <BitNav TItem="BitNavOption" Mode="BitNavMode.Manual"> - <ItemTemplate Context="option"> - <div class="nav-custom-item"> - <BitCheckbox /> - <BitIcon IconName="@option.IconName" /> - <span>@option.Text</span> - </div> - </ItemTemplate> - <ChildContent> - <BitNavOption Text="Fast foods" Description="List of fast foods" - IconName="@BitIconName.HeartBroken" IsExpanded="true"> - <BitNavOption Text="Burgers" Description="List of burgers"> - <BitNavOption Text="Beef Burger" /> - <BitNavOption Text="Veggie Burger" /> - <BitNavOption Text="Bison Burger" /> - <BitNavOption Text="Wild Salmon Burger" /> - </BitNavOption> - <BitNavOption Text="Pizza"> - <BitNavOption Text="Cheese Pizza" /> - <BitNavOption Text="Veggie Pizza" /> - <BitNavOption Text="Pepperoni Pizza" /> - <BitNavOption Text="Meat Pizza" /> - </BitNavOption> - <BitNavOption Text="French Fries" /> - </BitNavOption> - <BitNavOption Text="Fruits" IconName="@BitIconName.Health"> - <BitNavOption Text="Aplle" /> - <BitNavOption Text="Orange" /> - <BitNavOption Text="Benana" /> - </BitNavOption> - <BitNavOption Text="Ice Cream" /> - <BitNavOption Text="Cookie" /> - </ChildContent> - </BitNav> - </div> - </div> - </ExamplePreview> -</ComponentExampleBox> - -<ComponentExampleBox Title="Events" RazorCode="@example5NavOptionRazorCode" CsharpCode="@example5NavOptionCsharpCode" Id="example5"> - <ExamplePreview> - <div class="events-example-box"> <BitNav Mode="BitNavMode.Manual" OnItemClick="(BitNavOption option) => ClickedOption = option" OnSelectItem="(BitNavOption option) => SelectedOption = option" @@ -268,85 +300,78 @@ <BitNavOption Text="Ice Cream" Key="Ice Cream" /> <BitNavOption Text="Cookie" Key="Cookie" /> </BitNav> - - <div class="flex"> - <span>Clicked Option: <b>@ClickedOption?.Text</b></span> - <span>Selected Option: <b>@SelectedOption?.Text</b></span> + <div> + <span>Clicked Option: <b>@ClickedOption?.Text</b></span><br /> + <span>Selected Option: <b>@SelectedOption?.Text</b></span><br /> <span>Toggled Option: <b>@(ToggledOption is null ? "N/A" : $"{ToggledOption.Text} ({(ToggledOption.IsExpanded ? "Expanded" : "Collapsed")})")</b></span> </div> </div> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="Custom Styles" RazorCode="@example6NavOptionRazorCode" Id="example6"> +<ComponentExampleBox Title="Custom Styles" RazorCode="@example8RazorCode" Id="example8"> <ExamplePreview> - <div class="example-box"> - <BitNav TItem="BitNavOption" - Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", - ToggleButton = "color: cyan;", - Item = "color: red;", - ItemIcon = "color: gold; margin-right: 15px;" })"> - <BitNavOption Text="bit platform" - ExpandAriaLabel="bit platform Expanded" - CollapseAriaLabel="bit platform Collapsed"> - <BitNavOption Text="Home" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> - <BitNavOption Text="Products & Services"> - <BitNavOption Text="Project Templates"> - <BitNavOption IconName="@BitIconName.ToDoLogoOutline" Text="Todo sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - <BitNavOption IconName="@BitIconName.LocalAdmin" Text="AdminPanel sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" Url="https://bitplatform.dev/components" Target="_blank" /> - <BitNavOption Text="Cloud hosting solutions" IconName="@BitIconName.Cloud" IsEnabled="false" /> - <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> + <BitNav TItem="BitNavOption" + Styles="@(new() { ItemContainer = "border: 1px solid green; margin: 2px;", + ToggleButton = "color: cyan;", + Item = "color: red;", + ItemIcon = "color: gold; margin-right: 15px;" })"> + <BitNavOption Text="bit platform" + Description="the bit platform description" + ExpandAriaLabel="bit platform Expanded" + CollapseAriaLabel="bit platform Collapsed"> + <BitNavOption Text="Home" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> + <BitNavOption Text="Products & Services"> + <BitNavOption Text="Project Templates"> + <BitNavOption IconName="@BitIconName.ToDoLogoOutline" Text="Todo sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> + <BitNavOption IconName="@BitIconName.LocalAdmin" Text="AdminPanel sample" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> </BitNavOption> - <BitNavOption Text="Pricing" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> - <BitNavOption Text="About" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> - <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + <BitNavOption Text="BlazorUI" IconName="@BitIconName.F12DevTools" Url="https://bitplatform.dev/components" Target="_blank" /> + <BitNavOption Text="Cloud hosting solutions" IconName="@BitIconName.Cloud" IsEnabled="false" /> + <BitNavOption Text="Bit academy" IconName="@BitIconName.LearningTools" IsEnabled="false" /> </BitNavOption> - - <BitNavOption Text="Community" - ExpandAriaLabel="Community Expanded" - CollapseAriaLabel="Community Collapsed"> - <BitNavOption Text="LinkedIn" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> - <BitNavOption Text="Twitter" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> - <BitNavOption Text="GitHub repo" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> - </BitNavOption> - - <BitNavOption Text="Iconography" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> - </BitNav> - </div> + <BitNavOption Text="Pricing" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> + <BitNavOption Text="About" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> + <BitNavOption Text="Contact us" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Community" + ExpandAriaLabel="Community Expanded" + CollapseAriaLabel="Community Collapsed"> + <BitNavOption Text="LinkedIn" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> + <BitNavOption Text="Twitter" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> + <BitNavOption Text="GitHub repo" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="Iconography" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> + </BitNav> </ExamplePreview> </ComponentExampleBox> -<ComponentExampleBox Title="RTL" RazorCode="@example7NavOptionRazorCode" Id="example7"> +<ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> <div dir="rtl"> - <div class="example-box"> - <BitNav Dir="BitDir.Rtl" TItem="BitNavOption"> - <BitNavOption Text="پلتفرمِ بیت" Description="توضیحاتِ پلتفرمِ بیت"> - <BitNavOption Text="خانه" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> - <BitNavOption Text="محصولات و خدمات"> - <BitNavOption Text="قالب های پروژه"> - <BitNavOption IconName="@BitIconName.ToDoLogoOutline" Text="نمونه ی Todo" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - <BitNavOption IconName="@BitIconName.LocalAdmin" Text="نمونه ی AdminPanel" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="رابط کاربری Blazor" IconName="@BitIconName.F12DevTools" Url="https://blazorui.bitplatform.dev/" Target="_blank" /> - <BitNavOption Text="راه های هاست ابری" IconName="@BitIconName.Cloud" IsEnabled="false" /> - <BitNavOption Text="آکادمی بیت" IconName="@BitIconName.LearningTools" IsEnabled="false" /> + <BitNav Dir="BitDir.Rtl" TItem="BitNavOption"> + <BitNavOption Text="پلتفرمِ بیت" Description="توضیحاتِ پلتفرمِ بیت"> + <BitNavOption Text="خانه" IconName="@BitIconName.Home" Url="https://bitplatform.dev/" Target="_blank" /> + <BitNavOption Text="محصولات و خدمات"> + <BitNavOption Text="قالب های پروژه"> + <BitNavOption IconName="@BitIconName.ToDoLogoOutline" Text="نمونه ی Todo" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> + <BitNavOption IconName="@BitIconName.LocalAdmin" Text="نمونه ی AdminPanel" Url="https://bitplatform.dev/templates/overview" Target="_blank" /> </BitNavOption> - <BitNavOption Text="قیمت" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> - <BitNavOption Text="درباره ما" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> - <BitNavOption Text="ارتباط با ما" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> - </BitNavOption> - <BitNavOption Text="انجمن ها"> - <BitNavOption Text="لینکدین" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> - <BitNavOption Text="توییتر" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> - <BitNavOption Text="گیتهاب" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + <BitNavOption Text="رابط کاربری Blazor" IconName="@BitIconName.F12DevTools" Url="https://blazorui.bitplatform.dev/" Target="_blank" /> + <BitNavOption Text="راه های هاست ابری" IconName="@BitIconName.Cloud" IsEnabled="false" /> + <BitNavOption Text="آکادمی بیت" IconName="@BitIconName.LearningTools" IsEnabled="false" /> </BitNavOption> - - <BitNavOption Text="شمایل نگاری" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> - </BitNav> - </div> + <BitNavOption Text="قیمت" IconName="@BitIconName.Money" Url="https://bitplatform.dev/pricing" Target="_blank" /> + <BitNavOption Text="درباره ما" IconName="@BitIconName.Info" Url="https://bitplatform.dev/about-us" Target="_blank" /> + <BitNavOption Text="ارتباط با ما" IconName="@BitIconName.Contact" Url="https://bitplatform.dev/contact-us" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="انجمن ها"> + <BitNavOption Text="لینکدین" IconName="@BitIconName.LinkedInLogo" Url="https://www.linkedin.com/company/bitplatformhq" Target="_blank" /> + <BitNavOption Text="توییتر" IconName="@BitIconName.Globe" Url="https://twitter.com/bitplatformhq" Target="_blank" /> + <BitNavOption Text="گیتهاب" IconName="@BitIconName.GitGraph" Url="https://github.com/bitfoundation/bitplatform" Target="_blank" /> + </BitNavOption> + <BitNavOption Text="شمایل نگاری" IconName="@BitIconName.AppIconDefault" Url="/iconography" Target="_blank" /> + </BitNav> </div> </ExamplePreview> </ComponentExampleBox> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor.cs index bdca6e6bf7..57695c1a20 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Nav/_BitNavOptionDemo.razor.cs @@ -2,11 +2,13 @@ public partial class _BitNavOptionDemo { + private bool iconOnly; + private BitNavOption ClickedOption = default!; private BitNavOption ToggledOption = default!; private BitNavOption SelectedOption = default!; - private string? SelectedOptionKey; + private string? SelectedOptionKey = "French Fries"; private static readonly List<BitDropdownItem<string>> FoodMenuDropdownItems = [ new() { Text = "Beef Burger", Value = "Beef Burger" }, @@ -27,16 +29,16 @@ public partial class _BitNavOptionDemo - private readonly string example1NavOptionRazorCode = @" + private readonly string example1RazorCode = @" <BitNav TItem=""BitNavOption""> <BitNavOption Text=""bit platform"" - ExpandAriaLabel=""bit platform Expanded"" - CollapseAriaLabel=""bit platform Collapsed""> + ExpandAriaLabel=""bit platform Expanded"" + CollapseAriaLabel=""bit platform Collapsed""> <BitNavOption Text=""Home"" IconName=""@BitIconName.Home"" Url=""https://bitplatform.dev/"" Target=""_blank"" /> <BitNavOption Text=""Products & Services""> <BitNavOption Text=""Project Templates""> - <BitNavOption IconName=""@BitIconName.ToDoLogoOutline"" Text=""Todo sample"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> - <BitNavOption IconName=""@BitIconName.LocalAdmin"" Text=""AdminPanel sample"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> + <BitNavOption Text=""Todo sample"" IconName=""@BitIconName.ToDoLogoOutline"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> + <BitNavOption Text=""AdminPanel sample"" IconName=""@BitIconName.LocalAdmin"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> </BitNavOption> <BitNavOption Text=""BlazorUI"" IconName=""@BitIconName.F12DevTools"" Url=""https://bitplatform.dev/components"" Target=""_blank"" /> <BitNavOption Text=""Cloud hosting solutions"" IconName=""@BitIconName.Cloud"" IsEnabled=""false"" /> @@ -47,23 +49,51 @@ public partial class _BitNavOptionDemo <BitNavOption Text=""Contact us"" IconName=""@BitIconName.Contact"" Url=""https://bitplatform.dev/contact-us"" Target=""_blank"" /> </BitNavOption> <BitNavOption Text=""Community"" - ExpandAriaLabel=""Community Expanded"" - CollapseAriaLabel=""Community Collapsed""> + ExpandAriaLabel=""Community Expanded"" + CollapseAriaLabel=""Community Collapsed""> <BitNavOption Text=""Linkedin"" IconName=""@BitIconName.LinkedInLogo"" Url=""https://www.linkedin.com/company/bitplatformhq"" Target=""_blank"" /> <BitNavOption Text=""Twitter"" IconName=""@BitIconName.Globe"" Url=""https://twitter.com/bitplatformhq"" Target=""_blank"" /> <BitNavOption Text=""Github repo"" IconName=""@BitIconName.GitGraph"" Url=""https://github.com/bitfoundation/bitplatform"" Target=""_blank"" /> </BitNavOption> + <BitNavOption Text=""Iconography"" IconName=""@BitIconName.AppIconDefault"" Url=""/iconography"" Target=""_blank"" /> +</BitNav>"; + private readonly string example2RazorCode = @" +<BitNav TItem=""BitNavOption"" FitWidth> + <BitNavOption Text=""bit platform"" + ExpandAriaLabel=""bit platform Expanded"" + CollapseAriaLabel=""bit platform Collapsed""> + <BitNavOption Text=""Home"" IconName=""@BitIconName.Home"" Url=""https://bitplatform.dev/"" Target=""_blank"" /> + <BitNavOption Text=""Products & Services""> + <BitNavOption Text=""Project Templates""> + <BitNavOption Text=""Todo sample"" IconName=""@BitIconName.ToDoLogoOutline"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> + <BitNavOption Text=""AdminPanel sample"" IconName=""@BitIconName.LocalAdmin"" Url=""https://bitplatform.dev/templates/overview"" Target=""_blank"" /> + </BitNavOption> + <BitNavOption Text=""BlazorUI"" IconName=""@BitIconName.F12DevTools"" Url=""https://bitplatform.dev/components"" Target=""_blank"" /> + <BitNavOption Text=""Cloud hosting solutions"" IconName=""@BitIconName.Cloud"" IsEnabled=""false"" /> + <BitNavOption Text=""Bit academy"" IconName=""@BitIconName.LearningTools"" IsEnabled=""false"" /> + </BitNavOption> + <BitNavOption Text=""Pricing"" IconName=""@BitIconName.Money"" Url=""https://bitplatform.dev/pricing"" Target=""_blank"" /> + <BitNavOption Text=""About"" IconName=""@BitIconName.Info"" Url=""https://bitplatform.dev/about-us"" Target=""_blank"" /> + <BitNavOption Text=""Contact us"" IconName=""@BitIconName.Contact"" Url=""https://bitplatform.dev/contact-us"" Target=""_blank"" /> + </BitNavOption> + <BitNavOption Text=""Community"" + ExpandAriaLabel=""Community Expanded"" + CollapseAriaLabel=""Community Collapsed""> + <BitNavOption Text=""Linkedin"" IconName=""@BitIconName.LinkedInLogo"" Url=""https://www.linkedin.com/company/bitplatformhq"" Target=""_blank"" /> + <BitNavOption Text=""Twitter"" IconName=""@BitIconName.Globe"" Url=""https://twitter.com/bitplatformhq"" Target=""_blank"" /> + <BitNavOption Text=""Github repo"" IconName=""@BitIconName.GitGraph"" Url=""https://github.com/bitfoundation/bitplatform"" Target=""_blank"" /> + </BitNavOption> <BitNavOption Text=""Iconography"" IconName=""@BitIconName.AppIconDefault"" Url=""/iconography"" Target=""_blank"" /> </BitNav>"; - private readonly string example2NavOptionRazorCode = @" -<BitNav TItem=""BitNavOption"" RenderType=""BitNavRenderType.Grouped""> + private readonly string example3RazorCode = @" +< BitNav TItem=""BitNavOption"" RenderType=""BitNavRenderType.Grouped""> <BitNavOption Text=""Mercedes-Benz"" - ExpandAriaLabel=""Mercedes-Benz Expanded"" - CollapseAriaLabel=""Mercedes-Benz Collapsed"" - Title=""Mercedes-Benz Car Models"" - IsExpanded=""true""> + ExpandAriaLabel=""Mercedes-Benz Expanded"" + CollapseAriaLabel=""Mercedes-Benz Collapsed"" + Title=""Mercedes-Benz Car Models"" + IsExpanded=""true""> <BitNavOption Text=""SUVs""> <BitNavOption Text=""GLA"" Url=""https://www.mbusa.com/en/vehicles/class/gla/suv"" Target=""_blank"" /> <BitNavOption Text=""GLB"" Url=""https://www.mbusa.com/en/vehicles/class/glb/suv"" Target=""_blank"" /> @@ -81,17 +111,16 @@ public partial class _BitNavOptionDemo </BitNavOption> </BitNavOption> <BitNavOption Text=""Tesla"" - ExpandAriaLabel=""Tesla Expanded"" - CollapseAriaLabel=""Tesla Collapsed"" - Title=""Tesla Car Models""> + ExpandAriaLabel=""Tesla Expanded"" + CollapseAriaLabel=""Tesla Collapsed"" + Title=""Tesla Car Models""> <BitNavOption Text=""Model S"" Url=""https://www.tesla.com/models"" Target=""_blank"" /> <BitNavOption Text=""Model X"" Url=""https://www.tesla.com/modelx"" Target=""_blank"" /> <BitNavOption Text=""Model Y"" Url=""https://www.tesla.com/modely"" Target=""_blank"" /> </BitNavOption> </BitNav>"; - private readonly string example3NavOptionRazorCode = @" -<BitLabel>Basic</BitLabel> + private readonly string example4RazorCode = @" <BitNav TItem=""BitNavOption"" Mode=""BitNavMode.Manual""> <BitNavOption Text=""Fast foods"" Description=""List of fast foods"" IconName=""@BitIconName.HeartBroken"" IsExpanded=""true""> @@ -118,8 +147,8 @@ public partial class _BitNavOptionDemo <BitNavOption Text=""Cookie"" Key=""Cookie"" /> </BitNav> -<BitLabel>Two-Way Bind</BitLabel> -<BitNav TItem=""BitNavOption"" Mode=""BitNavMode.Manual""> +<BitNav Mode=""BitNavMode.Manual"" + OnSelectItem=""(BitNavOption option) => SelectedOptionKey = option.Key""> <BitNavOption Text=""Fast foods"" Description=""List of fast foods"" IconName=""@BitIconName.HeartBroken"" IsExpanded=""true""> <BitNavOption Text=""Burgers"" Description=""List of burgers""> @@ -146,10 +175,10 @@ public partial class _BitNavOptionDemo </BitNav> <BitDropdown @bind-Value=""SelectedOptionKey"" - DefaultValue=""French Fries"" - Label=""Pick one"" + FitWidth + Label=""Select Item"" Items=""FoodMenuDropdownItems"" />"; - private readonly string example3NavOptionCsharpCode = @" + private readonly string example4CsharpCode = @" private string SelectedOptionKey; private static readonly List<BitDropdownItem<string>> FoodMenuDropdownItems = @@ -170,8 +199,24 @@ public partial class _BitNavOptionDemo new() { Text = ""Cookie"", Value = ""Cookie"" }, ];"; - private readonly string example4NavOptionRazorCode = @" -<BitLabel>Header Template (in Grouped mode)</BitLabel> + private readonly string example5RazorCode = @" +<BitToggle @bind-Value=""iconOnly"" Label=""Hide texts?"" Inline /> +<BitNav TItem=""BitNavOption"" Mode=""BitNavMode.Manual"" IconOnly=""iconOnly""> + <BitNavOption Text=""Home"" IconName=""@BitIconName.Home"" /> + <BitNavOption Text=""AdminPanel sample"" IconName=""@BitIconName.LocalAdmin""> + <BitNavOption Text=""Dashboard"" IconName=""@BitIconName.ViewDashboard"" /> + <BitNavOption Text=""Categories"" IconName=""@BitIconName.BuildQueue"" /> + <BitNavOption Text=""Products"" IconName=""@BitIconName.Product"" /> + </BitNavOption> + <BitNavOption Text=""Todo sample"" IconName=""@BitIconName.ToDoLogoOutline"" /> + <BitNavOption Text=""BlazorUI"" IconName=""@BitIconName.F12DevTools"" /> + <BitNavOption Text=""Bit academy"" IconName=""@BitIconName.LearningTools"" IsEnabled=""false"" /> + <BitNavOption Text=""Contact us"" IconName=""@BitIconName.Contact"" /> +</BitNav>"; + private readonly string example5CsharpCode = @" +private bool iconOnly;"; + + private readonly string example6RazorCode = @" <BitNav TItem=""BitNavOption"" RenderType=""BitNavRenderType.Grouped""> <HeaderTemplate Context=""item""> <div class=""nav-custom-header""> @@ -212,7 +257,6 @@ public partial class _BitNavOptionDemo </ChildContent> </BitNav> -<BitLabel>Option Template</BitLabel> <BitNav TItem=""BitNavOption"" Mode=""BitNavMode.Manual""> <ItemTemplate Context=""option""> <div class=""nav-custom-item""> @@ -248,7 +292,7 @@ public partial class _BitNavOptionDemo </ChildContent> </BitNav>"; - private readonly string example5NavOptionRazorCode = @" + private readonly string example7RazorCode = @" <BitNav Mode=""BitNavMode.Manual"" OnItemClick=""(BitNavOption option) => ClickedOption = option"" OnSelectItem=""(BitNavOption option) => SelectedOption = option"" @@ -281,12 +325,12 @@ public partial class _BitNavOptionDemo <span>Clicked Option: @ClickedOption?.Text</span> <span>Selected Option: @SelectedOption?.Text</span> <span>Toggled Option: @(ToggledOption is null ? ""N/A"" : $""{ToggledOption.Text} ({(ToggledOption.IsExpanded ? ""Expanded"" : ""Collapsed"")})"")</span>"; - private readonly string example5NavOptionCsharpCode = @" + private readonly string example7CsharpCode = @" private BitNavOption ClickedOption; private BitNavOption SelectedOption; private BitNavOption ToggledOption;"; - private readonly string example6NavOptionRazorCode = @" + private readonly string example8RazorCode = @" <BitNav TItem=""BitNavOption"" Styles=""@(new() { ItemContainer = ""border: 1px solid green; margin: 2px;"", ToggleButton = ""color: cyan;"", @@ -309,7 +353,6 @@ public partial class _BitNavOptionDemo <BitNavOption Text=""About"" IconName=""@BitIconName.Info"" Url=""https://bitplatform.dev/about-us"" Target=""_blank"" /> <BitNavOption Text=""Contact us"" IconName=""@BitIconName.Contact"" Url=""https://bitplatform.dev/contact-us"" Target=""_blank"" /> </BitNavOption> - <BitNavOption Text=""Community"" ExpandAriaLabel=""Community Expanded"" CollapseAriaLabel=""Community Collapsed""> @@ -317,11 +360,10 @@ public partial class _BitNavOptionDemo <BitNavOption Text=""Twitter"" IconName=""@BitIconName.Globe"" Url=""https://twitter.com/bitplatformhq"" Target=""_blank"" /> <BitNavOption Text=""Github repo"" IconName=""@BitIconName.GitGraph"" Url=""https://github.com/bitfoundation/bitplatform"" Target=""_blank"" /> </BitNavOption> - <BitNavOption Text=""Iconography"" IconName=""@BitIconName.AppIconDefault"" Url=""/iconography"" Target=""_blank"" /> </BitNav>"; - private readonly string example7NavOptionRazorCode = @" + private readonly string example9RazorCode = @" <BitNav Dir=""BitDir.Rtl"" TItem=""BitNavOption""> <BitNavOption Text=""پلتفرمِ بیت"" Description=""توضیحاتِ پلتفرمِ بیت""> <BitNavOption Text=""خانه"" IconName=""@BitIconName.Home"" Url=""https://bitplatform.dev/"" Target=""_blank"" /> @@ -343,7 +385,6 @@ public partial class _BitNavOptionDemo <BitNavOption Text=""توییتر"" IconName=""@BitIconName.Globe"" Url=""https://twitter.com/bitplatformhq"" Target=""_blank"" /> <BitNavOption Text=""گیتهاب"" IconName=""@BitIconName.GitGraph"" Url=""https://github.com/bitfoundation/bitplatform"" Target=""_blank"" /> </BitNavOption> - <BitNavOption Text=""شمایل نگاری"" IconName=""@BitIconName.AppIconDefault"" Url=""/iconography"" Target=""_blank"" /> </BitNav>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.cs index 692e5371bb..edd7654613 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.cs @@ -46,15 +46,15 @@ public partial class BitPaginationDemo new() { Name = "FirstIcon", - Type = "string", - DefaultValue = "ChevronLeftEnd6", + Type = "string?", + DefaultValue = "null", Description = "The icon name of the first button." }, new() { Name = "LastIcon", - Type = "string", - DefaultValue = "ChevronRightEnd6", + Type = "string?", + DefaultValue = "null", Description = "The icon name of the last button." }, new() @@ -67,8 +67,8 @@ public partial class BitPaginationDemo new() { Name = "NextIcon", - Type = "string", - DefaultValue = "ChevronRight", + Type = "string?", + DefaultValue = "null", Description = "The icon name of the next button." }, new() @@ -81,8 +81,8 @@ public partial class BitPaginationDemo new() { Name = "PreviousIcon", - Type = "string", - DefaultValue = "ChevronLeft", + Type = "string?", + DefaultValue = "null", Description = "The icon name of the previous button." }, new() diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor index d927db6ad1..2898cb4619 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor @@ -13,209 +13,140 @@ <ExamplePreview> <BitPivot> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h3>Pivot #1</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h3>Pivot #2</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Count and Icon" RazorCode="@example2RazorCode" Id="example2"> + <ComponentExampleBox Title="Icon and Count" RazorCode="@example2RazorCode" Id="example2"> <ExamplePreview> - <BitPivot OverflowBehavior="@BitPivotOverflowBehavior.Scroll"> + <BitPivot> <BitPivotItem HeaderText="Files" IconName="@BitIconName.Info"> - <div style="margin-top:10px"> - <h1>Pivot #1: Files</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Files</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me" ItemCount="32"> - <div style="margin-top:10px"> - <h1>Pivot #2: Shared with me</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Shared with me</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent" IconName="@BitIconName.Info" ItemCount="12"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Some tab" IconName="@BitIconName.Info" ItemCount="6"> - <div style="margin-top:10px"> - <h1>Pivot #4: Some tab</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Latest" IconName="@BitIconName.Info" ItemCount="8"> - <div style="margin-top:10px"> - <h1>Pivot #5: Latest</h1> - <p>Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus. Suspendisse blandit erat ac lobortis pulvinar.</p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Large link size" RazorCode="@example3RazorCode" Id="example3"> + <ComponentExampleBox Title="Size" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> - <BitPivot LinkSize="@BitSize.Large"> + <BitPivot Size="@BitSize.Large"> <BitPivotItem HeaderText="Large File"> - <div style="margin-top:10px"> - <h1>Pivot #1: Large File</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Large File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Large Shared with me"> - <div style="margin-top:10px"> - <h1>Pivot #2: Large Shared with me</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Large Shared with me</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText="Large Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Large Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Large Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Tab style" RazorCode="@example4RazorCode" Id="example4"> + <ComponentExampleBox Title="HeaderType" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> - <BitPivot LinkFormat="@BitPivotLinkFormat.Tabs"> + <BitPivot HeaderType="@BitPivotHeaderType.Tab"> <BitPivotItem HeaderText="File tab"> - <div style="margin-top:10px"> - <h1>Pivot #1: File tab</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: File tab</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me tab"> - <div style="margin-top:10px"> - <h1>Pivot #2: Shared with me tab</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Shared with me tab</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent tab"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent tab</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> - </BitPivotItem> - </BitPivot> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Large Tab style" RazorCode="@example5RazorCode" Id="example5"> - <ExamplePreview> - <BitPivot LinkFormat="@BitPivotLinkFormat.Tabs" LinkSize="@BitSize.Large"> - <BitPivotItem HeaderText="Large File tab"> - <div style="margin-top:10px"> - <h1>Pivot #1: Large File tab</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Large Shared with me tab"> - <div style="margin-top:10px"> - <h1>Pivot #2: Large Shared with me tab</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Large Recent tab"> - <div style="margin-top:10px"> - <h1>Pivot #3: Large Recent tab</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Recent tab</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Change tabs from outside of the component" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="Binding" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <BitPivot @bind-SelectedKey="OverridePivotSelectedKey" LinkFormat="@BitPivotLinkFormat.Tabs" LinkSize="@BitSize.Large"> + <BitPivot @bind-SelectedKey="selectedKey"> <BitPivotItem Key="1" HeaderText="Samples"> - <div style="margin-top:10px"> - <h1>Pivot #1: Samples</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Samples</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem Key="2" HeaderText="Files"> - <div style="margin-top:10px"> - <h1>Pivot #2: Files</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Files</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem Key="3" HeaderText="Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> <BitPivotItem Key="4" HeaderText="Last"> - <div style="margin-top:10px"> - <h1>Pivot #4: Last</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi.</p> - </div> + <h1>Pivot #4: Last</h1> + <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi.</p> </BitPivotItem> </BitPivot> - <div style="margin-top:50px;display:flex;gap:10px"> - <BitButton IsEnabled="@(OverridePivotSelectedKey != "1")" Variant="BitVariant.Outline" - OnClick="(() => OverridePivotSelectedKey = (int.Parse(OverridePivotSelectedKey) - 1).ToString())"> - <div style="display:flex;gap:2px"> - <BitIcon IconName="@BitIconName.CaretSolidLeft" /> Prev - </div> - </BitButton> - <BitButton IsEnabled="@(OverridePivotSelectedKey != "4")" Variant="BitVariant.Outline" - OnClick="(() => OverridePivotSelectedKey = (int.Parse(OverridePivotSelectedKey) + 1).ToString())"> - <div style="display:flex;gap:2px"> - Next <BitIcon IconName="@BitIconName.CaretSolidRight" /> - </div> - </BitButton> - </div> + <br /><br /> + <BitButton Variant="BitVariant.Outline" + IconName="@BitIconName.CaretSolidLeft" + IsEnabled="@(selectedKey != "1")" + OnClick="(() => selectedKey = (int.Parse(selectedKey) - 1).ToString())"> + Prev + </BitButton> + <BitButton Variant="BitVariant.Outline" + IconName="@BitIconName.CaretSolidRight" + IsEnabled="@(selectedKey != "4")" + OnClick="(() => selectedKey = (int.Parse(selectedKey) + 1).ToString())"> + Next + </BitButton> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Render content separately" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> + <ComponentExampleBox Title="Detached" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> - <div style="margin-bottom:25px;border:1px solid #afafaf;width:fit-content;padding:10px;"> - @if (SelectedKey == "Foo") + <div style="border:1px solid gray;padding:10px;"> + @if (detachedSelectedKey == "Foo") { - <div>Hello I am Foo</div> + <div>Hello I am Fooooooooooooo</div> } - else if (SelectedKey == "Bar") + else if (detachedSelectedKey == "Bar") { - <div>Hello I am Bar</div> + <div>Hello I am Barrrrrrrrrrrr</div> } - else if (SelectedKey == "Bas") + else if (detachedSelectedKey == "Bas") { - <div>Hello I am Bas</div> + <div>Hello I am Bassssssssssss</div> } - else if (SelectedKey == "Biz") + else if (detachedSelectedKey == "Biz") { - <div>Hello I am Biz</div> + <div>Hello I am Bizzzzzzzzzzzz</div> } </div> - <BitPivot LinkFormat="@BitPivotLinkFormat.Tabs" + <br /> + <hr /> + <br /> + <BitPivot HeaderOnly="true" DefaultSelectedKey="Foo" - LinkSize="@BitSize.Large" - HeadersOnly="true" - OnItemClick="@(item => SelectedKey = item?.Key)"> + OnItemClick="@(item => detachedSelectedKey = item?.Key)"> <BitPivotItem HeaderText="Foo" Key="Foo"></BitPivotItem> <BitPivotItem HeaderText="Bar" Key="Bar"></BitPivotItem> <BitPivotItem HeaderText="Bas" Key="Bas"></BitPivotItem> @@ -224,410 +155,249 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Events" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> + <ComponentExampleBox Title="Events" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <div style="margin-bottom:25px"> - Last Pivot clicked: <strong>@SelectedPivotItem?.HeaderText</strong> - </div> - <BitPivot LinkFormat="@BitPivotLinkFormat.Tabs" LinkSize="@BitSize.Large" OnItemClick="@(item => SelectedPivotItem = item)"> + <BitPivot OnItemClick="@(item => selectedPivotItem = item)"> <BitPivotItem HeaderText="Foo"> - <div style="margin-top:10px"> - <h1>Pivot #1: Foo</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Foo</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Bar"> - <div style="margin-top:10px"> - <h1>Pivot #2: Bar</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Bar</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> - <BitPivotItem HeaderText="Bas" Key="aaa"> - <div style="margin-top:10px"> - <h1>Pivot #3: Bas</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <BitPivotItem HeaderText="Bas"> + <h1>Pivot #3: Bas</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> <BitPivotItem HeaderText="Biz"> - <div style="margin-top:10px"> - <h1>Pivot #4: Biz</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Baq"> - <div style="margin-top:10px"> - <h1>Pivot #5: Baq</h1> - <p>Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus. Suspendisse blandit erat ac lobortis pulvinar.</p> - </div> + <h1>Pivot #4: Biz</h1> + <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> </BitPivotItem> </BitPivot> + <br /><br /> + <div>Last header clicked: <b>@selectedPivotItem?.HeaderText</b></div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Show/Hide tabs" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> - <ExamplePreview> - <BitPivot LinkFormat="@BitPivotLinkFormat.Tabs" LinkSize="@BitSize.Large"> - <BitPivotItem HeaderText="Foo"> - <div style="margin-top:10px"> - <h1>Pivot #1: Foo</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Bar"> - <div style="margin-top:10px"> - <h1>Pivot #2: Bar</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> - </BitPivotItem> - <BitPivotItem Visibility="@PivotItemVisibility" HeaderText="Biz"> - <div style="margin-top:10px"> - <h1>Pivot #3: Biz</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> - </div> - </BitPivotItem> - </BitPivot> - <div style="margin-top:50px"> - <BitButton Variant="BitVariant.Outline" OnClick="TogglePivotItemVisibility">Hide/Show Biz</BitButton> - </div> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Custom Header" RazorCode="@example10RazorCode" Id="example10"> + <ComponentExampleBox Title="Templates" RazorCode="@example8RazorCode" Id="example8"> <ExamplePreview> <BitPivot> <BitPivotItem> <Header> - <div> - <span style="color:red">Header #1</span> - </div> + <span style="color:red">Header #1</span> </Header> <Body> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </Body> </BitPivotItem> <BitPivotItem ItemCount="99"> <Header> - <div> - <i style="color:green" class="bit-icon bit-icon--HeartFill"></i> - <span style="color:blue">Header #2</span> - <i style="color:green" class="bit-icon bit-icon--HeartFill"></i> - </div> + <i style="color:green" class="bit-icon bit-icon--HeartFill"></i> + <span style="color:blue">Header #2</span> + <i style="color:green" class="bit-icon bit-icon--HeartFill"></i> </Header> <Body> - <div style="margin-top:10px"> - <h1>Pivot #2</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </Body> </BitPivotItem> <BitPivotItem IconName="@BitIconName.Inbox"> <Header> - <div> - <span style="color:rebeccapurple">Header <i style="color:purple" class="bit-icon bit-icon--HeartFill"></i> #3</span> - </div> + <span style="color:rebeccapurple">Header <i style="color:purple" class="bit-icon bit-icon--HeartFill"></i> #3</span> </Header> <Body> - <div style="margin-top:10px"> - <h1>Pivot #3</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </Body> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Alignment" RazorCode="@example11RazorCode" Id="example11"> + <ComponentExampleBox Title="Alignment" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> <BitPivot Alignment="BitAlignment.Center"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. </p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. </p> </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Position" RazorCode="@example12RazorCode" Id="example12"> + <ComponentExampleBox Title="Position" RazorCode="@example10RazorCode" Id="example10"> <ExamplePreview> - <div class="subtitle">Pivot Position: <strong>Top</strong></div> + <div>Pivot Position: <b>Top</b></div><br /> <div class="box"> <BitPivot Position="BitPivotPosition.Top"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1: File</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> </BitPivotItem> <BitPivotItem HeaderText="Shared"> - <div style="margin-top:15px"> - <h1>Pivot #2: Shared</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> + <h1>Pivot #2: Shared</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> </BitPivotItem> </BitPivot> </div> - <br /> - <div class="subtitle">Pivot Position: <strong>Bottom</strong></div> + <br /><br /><br /> + <div>Pivot Position: <b>Bottom</b></div><br /> <div class="box"> <BitPivot Position="BitPivotPosition.Bottom"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1: File</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> </BitPivotItem> <BitPivotItem HeaderText="Shared"> - <div style="margin-top:15px"> - <h1>Pivot #2: Shared</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> + <h1>Pivot #2: Shared</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> </BitPivotItem> </BitPivot> </div> - <br /> - <div class="subtitle">Pivot Position: <strong>Left</strong></div> + <br /><br /><br /> + <div>Pivot Position: <b>Left</b></div><br /> <div class="box"> <BitPivot Position="BitPivotPosition.Left"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1: File</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me" Style="width:130px"> - <div style="margin-top:15px"> - <h1>Pivot #2: Shared with me</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> + <h1>Pivot #2: Shared with me</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> </BitPivotItem> </BitPivot> </div> - <br /> - <div class="subtitle">Pivot Position: <strong>Right</strong></div> + <br /><br /><br /> + <div>Pivot Position: <b>Right</b></div><br /> <div class="box"> <BitPivot Position="BitPivotPosition.Right"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1: File</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me" Style="width:130px"> - <div style="margin-top:15px"> - <h1>Pivot #2: Shared with me</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> + <h1>Pivot #2: Shared with me</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h1>Pivot #3: Recent</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> </BitPivotItem> </BitPivot> </div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Disabled state" RazorCode="@example13RazorCode" CsharpCode="@example13CsharpCode" Id="example13"> - <ExamplePreview> - <BitButton OnClick="() => PivotEnabled = !PivotEnabled">Toggle Pivot's IsEnabled</BitButton> - <BitButton OnClick="() => PivotItemEnabled = !PivotItemEnabled">Toggle Pivot Item's IsEnabled</BitButton> - <BitPivot IsEnabled="PivotEnabled"> - <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Shared with me" IsEnabled="PivotItemEnabled"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> - </BitPivotItem> - </BitPivot> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Style & Class" RazorCode="@example14RazorCode" Id="example14"> + <ComponentExampleBox Title="Style & Class" RazorCode="@example11RazorCode" Id="example11"> <ExamplePreview> <div> - <div>Component's Style & Class:</div> - <br /> + <div>Component's Style & Class:</div><br /> <BitPivot Style="border: 1px solid tomato;"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> - <br /> + <br /><br /> <BitPivot Class="custom-class"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> - <br /><br /><br /> - <div><b>Styles</b> & <b>Classes</b>:</div> - <br /> + <br /><br /><br /><br /> + <div><b>Styles</b> & <b>Classes</b>:</div><br /> <BitPivot Styles="@(new() { HeaderIcon = "color: tomato;", HeaderText = "color: purple;" })"> <BitPivotItem HeaderText="File" IconName="Info"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> - <br /> + <br /><br /><br /> <BitPivot Classes="@(new() { Body = "custom-body", SelectedItem = "custom-selected-item", Header = "custom-header" })"> <BitPivotItem HeaderText="File"> - <div style="margin-top:10px"> - <h1>Pivot #1</h1> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText="Shared with me"> - <div style="margin-top:15px"> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText="Recent"> - <div style="margin-top:10px"> - <h3>Pivot #3</h3> - <p style="white-space:pre-wrap">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> </div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example15RazorCode" Id="example15"> + <ComponentExampleBox Title="RTL" RazorCode="@example12RazorCode" Id="example12"> <ExamplePreview> <BitPivot Dir="BitDir.Rtl" OverflowBehavior="@BitPivotOverflowBehavior.Scroll"> <BitPivotItem HeaderText="اسناد" IconName="@BitIconName.Info"> - <br /> <p> لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. </p> </BitPivotItem> - <BitPivotItem HeaderText="آخرین ها" IconName="@BitIconName.Info" ItemCount="8"> - <br /> + <BitPivotItem HeaderText="آخرین ها" ItemCount="8"> <p> چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. </p> </BitPivotItem> <BitPivotItem HeaderText="شخصی" IconName="@BitIconName.Info" ItemCount="6"> - <br /> <p> کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. </p> </BitPivotItem> - <BitPivotItem HeaderText="اخیرا" IconName="@BitIconName.Info" ItemCount="12"> - <br /> - <p> - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - </BitPivotItem> </BitPivot> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> - -@code { - private string? SelectedKey = "Foo"; - private BitPivotItem SelectedPivotItem = default!; - private string OverridePivotSelectedKey = "1"; - private BitVisibility PivotItemVisibility; - private bool PivotEnabled = true; - private bool PivotItemEnabled = true; - - private void TogglePivotItemVisibility() - { - PivotItemVisibility = PivotItemVisibility == BitVisibility.Visible ? BitVisibility.Collapsed : BitVisibility.Visible; - } -} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs index 544a2e3b70..67b1a74e6f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs @@ -18,14 +18,14 @@ public partial class BitPivotDemo Name = "ChildContent", Type = "RenderFragment?", DefaultValue = "null", - Description = "The content of pivot, It can be Any custom tag.", + Description = "The content of pivot.", }, new() { Name = "Classes", Type = "BitPivotClassStyles?", DefaultValue = "null", - Description = "Custom CSS classes for different parts of the BitPivot component.", + Description = "Custom CSS classes for different parts of the pivot.", LinkType = LinkType.Link, Href = "#pivot-class-styles", }, @@ -38,28 +38,35 @@ public partial class BitPivotDemo }, new() { - Name = "HeadersOnly", + Name = "HeaderOnly", Type = "bool", DefaultValue = "false", Description = "Whether to skip rendering the tabpanel with the content of the selected tab.", }, new() { - Name = "LinkFormat", - Type = "BitPivotLinkFormat", - DefaultValue = "BitPivotLinkFormat.Links", - Description = "Pivot link format, display mode for the pivot links.", + Name = "HeaderType", + Type = "BitPivotHeaderType", + DefaultValue = "BitPivotHeaderType.Link", + Description = "The type of the pivot header items.", LinkType = LinkType.Link, - Href = "#linkFormat-enum", + Href = "#header-type-enum", }, new() { - Name = "LinkSize", - Type = "BitSize?", - DefaultValue = "null", - Description = "The size of the pivot links.", + Name = "OnItemClick", + Type = "EventCallback<BitPivotItem>", + Description = "Callback for when the a pivot item is clicked.", LinkType = LinkType.Link, - Href = "#linkSize-enum", + Href = "#pivot-item", + }, + new() + { + Name = "OnChange", + Type = "EventCallback<BitPivotItem>", + Description = "Callback for when the selected pivot item changes.", + LinkType = LinkType.Link, + Href = "#pivot-item", }, new() { @@ -71,14 +78,6 @@ public partial class BitPivotDemo Href = "#overflowBehavior-enum", }, new() - { - Name = "OnItemClick", - Type = "EventCallback<BitPivotItem>", - Description = "Callback for when the a pivot item is clicked.", - LinkType = LinkType.Link, - Href = "#pivotItem", - }, - new() { Name = "Position", Type = "BitPivotPosition", @@ -92,14 +91,23 @@ public partial class BitPivotDemo Name = "SelectedKey", Type = "string?", DefaultValue = "null", - Description = "Key of the selected pivot item. Updating this will override the Pivot's selected item state.", + Description = "Key of the selected pivot item.", + }, + new() + { + Name = "Size", + Type = "BitSize?", + DefaultValue = "null", + Description = "The size of the pivot header items.", + LinkType = LinkType.Link, + Href = "#size-enum", }, new() { Name = "Styles", Type = "BitPivotClassStyles?", DefaultValue = "null", - Description = "Custom CSS styles for different parts of the BitPivot component.", + Description = "Custom CSS styles for different parts of the pivot.", Href = "#pivot-class-styles", LinkType = LinkType.Link }, @@ -109,7 +117,7 @@ public partial class BitPivotDemo [ new() { - Id = "pivotItem", + Id = "pivot-item", Title = "BitPivotItem", Parameters = [ @@ -311,28 +319,28 @@ public partial class BitPivotDemo }, new() { - Id = "linkFormat-enum", - Name = "BitLinkFormat", + Id = "header-type-enum", + Name = "BitPivotHeaderType", Description = "", Items = [ new() { - Name= "Tabs", - Description="Display Pivot Links as Tabs.", + Name= "Tab", + Description="Renders pivot header items as Tab.", Value="0", }, new() { - Name= "Links", - Description="Display Pivot Links as links.", + Name= "Link", + Description="Renders pivot header items as link.", Value="1", }, ] }, new() { - Id = "linkSize-enum", + Id = "size-enum", Name = "BitSize", Description = "", Items = @@ -340,19 +348,19 @@ public partial class BitPivotDemo new() { Name= "Small", - Description="Display Link using small font size.", + Description="The small size.", Value="0", }, new() { Name= "Medium", - Description="Display Link using medium font size.", + Description="The medium size.", Value="1", }, new() { Name= "Large", - Description="Display links using large font size.", + Description="The large size.", Value="2", }, ] @@ -421,482 +429,277 @@ public partial class BitPivotDemo + private string selectedKey = "1"; + private string? detachedSelectedKey = "Foo"; + private BitPivotItem selectedPivotItem = default!; + + + private readonly string example1RazorCode = @" <BitPivot> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h3>Pivot #1</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h3>Pivot #2</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> </BitPivot>"; private readonly string example2RazorCode = @" -<BitPivot OverflowBehavior=""@BitPivotOverflowBehavior.Scroll""> +<BitPivot> <BitPivotItem HeaderText=""Files"" IconName=""@BitIconName.Info""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Files</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Files</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me"" ItemCount=""32""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Shared with me</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Shared with me</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent"" IconName=""@BitIconName.Info"" ItemCount=""12""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Some tab"" IconName=""@BitIconName.Info"" ItemCount=""6""> - <div style=""margin-top:10px""> - <h1>Pivot #4: Some tab</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Latest"" IconName=""@BitIconName.Info"" ItemCount=""8""> - <div style=""margin-top:10px""> - <h1>Pivot #5: Latest</h1> - <p>Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus. Suspendisse blandit erat ac lobortis pulvinar.</p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot>"; private readonly string example3RazorCode = @" -<BitPivot LinkSize=""@BitSize.Large""> +<BitPivot Size=""@BitSize.Large""> <BitPivotItem HeaderText=""Large File""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Large File</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Large File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Large Shared with me""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Large Shared with me</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Large Shared with me</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText=""Large Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Large Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Large Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot>"; private readonly string example4RazorCode = @" -<BitPivot LinkFormat=""@BitPivotLinkFormat.Tabs""> +<BitPivot HeaderType=""@BitPivotHeaderType.Tab""> <BitPivotItem HeaderText=""File tab""> - <div style=""margin-top:10px""> - <h1>Pivot #1: File tab</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: File tab</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me tab""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Shared with me tab</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Shared with me tab</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent tab""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent tab</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Recent tab</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> </BitPivot>"; private readonly string example5RazorCode = @" -<BitPivot LinkFormat=""@BitPivotLinkFormat.Tabs"" LinkSize=""@BitSize.Large""> - <BitPivotItem HeaderText=""Large File tab""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Large File tab</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Large Shared with me tab""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Large Shared with me tab</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Large Recent tab""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Large Recent tab</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> - </BitPivotItem> -</BitPivot>"; - - private readonly string example6RazorCode = @" -<BitPivot @bind-SelectedKey=""OverridePivotSelectedKey"" LinkFormat=""@BitPivotLinkFormat.Tabs"" LinkSize=""@BitSize.Large""> +<BitPivot @bind-SelectedKey=""selectedKey""> <BitPivotItem Key=""1"" HeaderText=""Samples""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Samples</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Samples</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem Key=""2"" HeaderText=""Files""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Files</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Files</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> <BitPivotItem Key=""3"" HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> <BitPivotItem Key=""4"" HeaderText=""Last""> - <div style=""margin-top:10px""> - <h1>Pivot #4: Last</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi.</p> - </div> + <h1>Pivot #4: Last</h1> + <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi.</p> </BitPivotItem> </BitPivot> -<div style=""margin-top:50px;display:flex;gap:10px""> - <BitButton IsEnabled=""@(OverridePivotSelectedKey != ""1"")"" Variant=""BitVariant.Outline"" - OnClick=""(() => OverridePivotSelectedKey = (int.Parse(OverridePivotSelectedKey) - 1).ToString())""> - <div style=""display:flex;gap:2px""> - <BitIcon IconName=""@BitIconName.CaretSolidLeft"" /> Prev - </div> - </BitButton> - <BitButton IsEnabled=""@(OverridePivotSelectedKey != ""4"")"" Variant=""BitVariant.Outline"" - OnClick=""(() => OverridePivotSelectedKey = (int.Parse(OverridePivotSelectedKey) + 1).ToString())""> - <div style=""display:flex;gap:2px""> - Next <BitIcon IconName=""@BitIconName.CaretSolidRight"" /> - </div> - </BitButton> -</div>"; - private readonly string example6CsharpCode = @" -private string OverridePivotSelectedKey = ""1"";"; +<BitButton Variant=""BitVariant.Outline"" + IconName=""@BitIconName.CaretSolidLeft"" + IsEnabled=""@(selectedKey != ""1"")"" + OnClick=""(() => selectedKey = (int.Parse(selectedKey) - 1).ToString())""> + Prev +</BitButton> +<BitButton Variant=""BitVariant.Outline"" + IconName=""@BitIconName.CaretSolidRight"" + IsEnabled=""@(selectedKey != ""4"")"" + OnClick=""(() => selectedKey = (int.Parse(selectedKey) + 1).ToString())""> + Next +</BitButton>"; + private readonly string example5CsharpCode = @" +private string selectedKey = ""1"";"; - private readonly string example7RazorCode = @" -<div style=""margin-bottom:25px;border:1px solid #afafaf;width:fit-content;padding:10px;""> - @if (SelectedKey == ""Foo"") + private readonly string example6RazorCode = @" +<div style=""border:1px solid gray;padding:10px;""> + @if (detachedSelectedKey == ""Foo"") { - <div>Hello I am Foo</div> + <div>Hello I am Fooooooooooooo</div> } - else if (SelectedKey == ""Bar"") + else if (detachedSelectedKey == ""Bar"") { - <div>Hello I am Bar</div> + <div>Hello I am Barrrrrrrrrrrr</div> } - else if (SelectedKey == ""Bas"") + else if (detachedSelectedKey == ""Bas"") { - <div>Hello I am Bas</div> + <div>Hello I am Bassssssssssss</div> } - else if (SelectedKey == ""Biz"") + else if (detachedSelectedKey == ""Biz"") { - <div>Hello I am Biz</div> + <div>Hello I am Bizzzzzzzzzzzz</div> } </div> - -<BitPivot LinkFormat=""@BitPivotLinkFormat.Tabs"" +<hr /> +<BitPivot HeaderOnly=""true"" DefaultSelectedKey=""Foo"" - LinkSize=""@BitSize.Large"" - HeadersOnly=""true"" - OnItemClick=""@(item => SelectedKey = item.Key)""> + OnItemClick=""@(item => detachedSelectedKey = item?.Key)""> <BitPivotItem HeaderText=""Foo"" Key=""Foo""></BitPivotItem> <BitPivotItem HeaderText=""Bar"" Key=""Bar""></BitPivotItem> <BitPivotItem HeaderText=""Bas"" Key=""Bas""></BitPivotItem> <BitPivotItem HeaderText=""Biz"" Key=""Biz""></BitPivotItem> </BitPivot>"; - private readonly string example7CsharpCode = @" -private string SelectedKey = ""Foo"";"; - - private readonly string example8RazorCode = @" -<div style=""margin-bottom:25px""> - Last Pivot clicked: <strong>@SelectedPivotItem?.HeaderText</strong> -</div> + private readonly string example6CsharpCode = @" +private string detachedSelectedKey = ""Foo"";"; -<BitPivot LinkFormat=""@BitPivotLinkFormat.Tabs"" LinkSize=""@BitSize.Large"" OnItemClick=""@(item => SelectedPivotItem = item)""> + private readonly string example7RazorCode = @" +<BitPivot OnItemClick=""@(item => selectedPivotItem = item)""> <BitPivotItem HeaderText=""Foo""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Foo</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1: Foo</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Bar""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Bar</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2: Bar</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </BitPivotItem> - <BitPivotItem HeaderText=""Bas"" Key=""aaa""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Bas</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> - </BitPivotItem> - <BitPivotItem Visibility=""@PivotItemVisibility"" HeaderText=""Biz""> - <div style=""margin-top:10px""> - <h1>Pivot #4: Biz</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> - </div> + <BitPivotItem HeaderText=""Bas""> + <h1>Pivot #3: Bas</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </BitPivotItem> - <BitPivotItem HeaderText=""Baq""> - <div style=""margin-top:10px""> - <h1>Pivot #5: Baq</h1> - <p>Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus. Suspendisse blandit erat ac lobortis pulvinar.</p> - </div> - </BitPivotItem> -</BitPivot>"; - private readonly string example8CsharpCode = @" -private BitPivotItem SelectedPivotItem;"; - - private readonly string example9RazorCode = @" -<BitPivot LinkFormat=""@BitPivotLinkFormat.Tabs"" LinkSize=""@BitSize.Large""> - <BitPivotItem HeaderText=""Foo""> - <div style=""margin-top:10px""> - <h1>Pivot #1: Foo</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Bar""> - <div style=""margin-top:10px""> - <h1>Pivot #2: Bar</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> - </BitPivotItem> - <BitPivotItem Visibility=""@PivotItemVisibility"" HeaderText=""Biz""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Biz</h1> - <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> - </div> + <BitPivotItem HeaderText=""Biz""> + <h1>Pivot #4: Biz</h1> + <p>Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan.</p> </BitPivotItem> </BitPivot> -<div style=""margin-top:50px""> - <BitButton Variant=""BitVariant.Outline"" OnClick=""TogglePivotItemVisibility"">Hide/Show Biz</BitButton> -</div>"; - private readonly string example9CsharpCode = @" -private BitVisibility PivotItemVisibility; -private void TogglePivotItemVisibility() -{ - PivotItemVisibility = PivotItemVisibility == BitVisibility.Visible ? BitVisibility.Collapsed : BitVisibility.Visible; -}"; +<div>Last header clicked: <b>@selectedPivotItem?.HeaderText</b></div>"; + private readonly string example7CsharpCode = @" +private BitPivotItem selectedPivotItem;"; - private readonly string example10RazorCode = @" + private readonly string example8RazorCode = @" <BitPivot> <BitPivotItem> <Header> - <div> - <span style=""color:red"">Header #1</span> - </div> + <span style=""color:red"">Header #1</span> </Header> <Body> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </Body> </BitPivotItem> <BitPivotItem ItemCount=""99""> <Header> - <div> - <i style=""color:green"" class=""bit-icon bit-icon--HeartFill""></i> - <span style=""color:blue"">Header #2</span> - <i style=""color:green"" class=""bit-icon bit-icon--HeartFill""></i> - </div> + <i style=""color:green"" class=""bit-icon bit-icon--HeartFill""></i> + <span style=""color:blue"">Header #2</span> + <i style=""color:green"" class=""bit-icon bit-icon--HeartFill""></i> </Header> <Body> - <div style=""margin-top:10px""> - <h1>Pivot #2</h1> - <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> - </div> + <h1>Pivot #2</h1> + <p>Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel.</p> </Body> </BitPivotItem> <BitPivotItem IconName=""@BitIconName.Inbox""> <Header> - <div> - <span style=""color:rebeccapurple"">Header <i style=""color:purple"" class=""bit-icon bit-icon--HeartFill""></i> #3</span> - </div> + <span style=""color:rebeccapurple"">Header <i style=""color:purple"" class=""bit-icon bit-icon--HeartFill""></i> #3</span> </Header> <Body> - <div style=""margin-top:10px""> - <h1>Pivot #3</h1> - <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> - </div> + <h1>Pivot #3</h1> + <p>Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum.</p> </Body> </BitPivotItem> </BitPivot>"; - private readonly string example11RazorCode = @" + private readonly string example9RazorCode = @" <BitPivot Alignment=""BitAlignment.Center""> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. </p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. </p> </BitPivotItem> </BitPivot>"; - private readonly string example12RazorCode = @" -<style> - .subtitle { - padding: 20px 0 10px 0; - } - - .box { - border: 1px solid #ccc; - padding: 5px 10px; - } -</style> - -<div class=""subtitle"">Pivot Position: <strong>Top</strong></div> -<div class=""box""> - <BitPivot Position=""BitPivotPosition.Top"" Style=""height:200px""> - <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1: File</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Shared""> - <div style=""margin-top:15px""> - <h1>Pivot #2: Shared</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> - </BitPivotItem> - </BitPivot> -</div> - -<div class=""subtitle"">Pivot Position: <strong>Bottom</strong></div> -<div class=""box""> - <BitPivot Position=""BitPivotPosition.Bottom"" Style=""height:200px""> - <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1: File</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Shared""> - <div style=""margin-top:15px""> - <h1>Pivot #2: Shared</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> - </BitPivotItem> - </BitPivot> -</div> - -<div class=""subtitle"">Pivot Position: <strong>Left</strong></div> -<div class=""box""> - <BitPivot Position=""BitPivotPosition.Left""> - <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1: File</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Shared with me"" Style=""width:130px""> - <div style=""margin-top:15px""> - <h1>Pivot #2: Shared with me</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> - </BitPivotItem> - </BitPivot> -</div> + private readonly string example10RazorCode = @" +<BitPivot Position=""BitPivotPosition.Top""> + <BitPivotItem HeaderText=""File""> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> + </BitPivotItem> + <BitPivotItem HeaderText=""Shared""> + <h1>Pivot #2: Shared</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> + </BitPivotItem> + <BitPivotItem HeaderText=""Recent""> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> + </BitPivotItem> +</BitPivot> -<div class=""subtitle"">Pivot Position: <strong>Right</strong></div> -<div class=""box""> - <BitPivot Position=""BitPivotPosition.Right""> - <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1: File</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Shared with me"" Style=""width:130px""> - <div style=""margin-top:15px""> - <h1>Pivot #2: Shared with me</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> - </div> - </BitPivotItem> - <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h1>Pivot #3: Recent</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> - </div> - </BitPivotItem> - </BitPivot> -</div>"; +<BitPivot Position=""BitPivotPosition.Bottom""> + <BitPivotItem HeaderText=""File""> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> + </BitPivotItem> + <BitPivotItem HeaderText=""Shared""> + <h1>Pivot #2: Shared</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> + </BitPivotItem> + <BitPivotItem HeaderText=""Recent""> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> + </BitPivotItem> +</BitPivot> - private readonly string example13RazorCode = @" -<BitButton OnClick=""() => PivotEnabled = !PivotEnabled"">Toggle Pivot's IsEnabled</BitButton> -<BitButton OnClick=""() => PivotItemEnabled = !PivotItemEnabled"">Toggle Pivot Item's IsEnabled</BitButton> +<BitPivot Position=""BitPivotPosition.Left""> + <BitPivotItem HeaderText=""File""> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> + </BitPivotItem> + <BitPivotItem HeaderText=""Shared with me"" Style=""width:130px""> + <h1>Pivot #2: Shared with me</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> + </BitPivotItem> + <BitPivotItem HeaderText=""Recent""> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> + </BitPivotItem> +</BitPivot> -<BitPivot IsEnabled=""PivotEnabled""> +<BitPivot Position=""BitPivotPosition.Right""> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1: File</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. </p> </BitPivotItem> - <BitPivotItem HeaderText=""Shared with me"" IsEnabled=""PivotItemEnabled""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <BitPivotItem HeaderText=""Shared with me"" Style=""width:130px""> + <h1>Pivot #2: Shared with me</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h1>Pivot #3: Recent</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. </p> </BitPivotItem> </BitPivot>"; - private readonly string example13CsharpCode = @" -private bool PivotEnabled = true; -private bool PivotItemEnabled = true;"; - private readonly string example14RazorCode = @" + private readonly string example11RazorCode = @" <style> .custom-class { margin: 1rem; @@ -920,116 +723,82 @@ private void TogglePivotItemVisibility() } </style> - <BitPivot Style=""border: 1px solid tomato;""> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> <BitPivot Class=""custom-class""> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> <BitPivot Styles=""@(new() { HeaderIcon = ""color: tomato;"", HeaderText = ""color: purple;"" })""> <BitPivotItem HeaderText=""File"" IconName=""Info""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot> <BitPivot Classes=""@(new() { Body = ""custom-body"", SelectedItem = ""custom-selected-item"", Header = ""custom-header"" })""> <BitPivotItem HeaderText=""File""> - <div style=""margin-top:10px""> - <h1>Pivot #1</h1> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> - </div> + <h1>Pivot #1</h1> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.</p> </BitPivotItem> <BitPivotItem HeaderText=""Shared with me""> - <div style=""margin-top:15px""> - <h2>Pivot #2</h2> - <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> - </div> + <h2>Pivot #2</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra.</p> </BitPivotItem> <BitPivotItem HeaderText=""Recent""> - <div style=""margin-top:10px""> - <h3>Pivot #3</h3> - <p style=""white-space:pre-wrap"">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> - </div> + <h3>Pivot #3</h3> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eu ligula quis orci accumsan pharetra. Fusce mattis sit amet enim vitae imperdiet. Maecenas hendrerit sapien nisl, quis consectetur mi bibendum vel. Pellentesque vel rhoncus quam, non bibendum arcu. Vivamus euismod tellus non felis finibus, dictum finibus eros elementum. Vivamus a massa sit amet leo volutpat blandit at vel tortor. Praesent posuere, nulla eu tempus accumsan, nibh elit rhoncus mauris, eu semper tellus risus et nisi. Duis felis ipsum, luctus eget ultrices sit amet, scelerisque quis metus.<br />Suspendisse blandit erat ac lobortis pulvinar. Donec nunc leo, tempus sit amet accumsan in, sagittis sed odio. Pellentesque tristique felis sed purus pellentesque, ac dictum ex fringilla. Integer a tincidunt eros, non porttitor turpis. Sed gravida felis massa, in viverra massa aliquam sit amet. Etiam vitae dolor in velit sodales tristique id nec turpis. Proin sit amet urna sollicitudin, malesuada enim et, lacinia mi. Fusce nisl massa, efficitur sit amet elementum convallis, porttitor vel turpis. Fusce congue dui sit amet mollis pulvinar. Suspendisse vulputate leo quis nunc tincidunt, nec dictum risus congue.</p> </BitPivotItem> </BitPivot>"; - private readonly string example15RazorCode = @" + private readonly string example12RazorCode = @" <BitPivot Dir=""BitDir.Rtl"" OverflowBehavior=""@BitPivotOverflowBehavior.Scroll""> <BitPivotItem HeaderText=""اسناد"" IconName=""@BitIconName.Info""> - <br /> <p> لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. </p> </BitPivotItem> - <BitPivotItem HeaderText=""آخرین ها"" IconName=""@BitIconName.Info"" ItemCount=""8""> - <br /> + <BitPivotItem HeaderText=""آخرین ها"" ItemCount=""8""> <p> چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. </p> </BitPivotItem> <BitPivotItem HeaderText=""شخصی"" IconName=""@BitIconName.Info"" ItemCount=""6""> - <br /> <p> کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. </p> </BitPivotItem> - <BitPivotItem HeaderText=""اخیرا"" IconName=""@BitIconName.Info"" ItemCount=""12""> - <br /> - <p> - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - </BitPivotItem> </BitPivot>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor index 9f0b353488..429037b49b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor @@ -28,6 +28,21 @@ <BitMessage Color="BitColor.SevereWarning">SevereWarning.</BitMessage> <BitMessage Color="BitColor.Error">Error.</BitMessage> </div> + <div style="background:var(--bit-clr-fg-sec);padding:1rem;margin: 1rem 0;"> + <div class="box"> + <BitMessage Color="BitColor.PrimaryBackground">PrimaryBackground.</BitMessage> + <BitMessage Color="BitColor.SecondaryBackground">SecondaryBackground.</BitMessage> + <BitMessage Color="BitColor.TertiaryBackground">TertiaryBackground.</BitMessage> + </div> + </div> + <div class="box"> + <BitMessage Color="BitColor.PrimaryForeground">PrimaryForeground.</BitMessage> + <BitMessage Color="BitColor.SecondaryForeground">SecondaryForeground.</BitMessage> + <BitMessage Color="BitColor.TertiaryForeground">TertiaryForeground.</BitMessage> + <BitMessage Color="BitColor.PrimaryBorder">PrimaryBorder.</BitMessage> + <BitMessage Color="BitColor.SecondaryBorder">SecondaryBorder.</BitMessage> + <BitMessage Color="BitColor.TertiaryBorder">TertiaryBorder.</BitMessage> + </div> </ExamplePreview> </ComponentExampleBox> @@ -71,7 +86,28 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Multiline" RazorCode="@example4RazorCode" Id="example4"> + <ComponentExampleBox Title="Alignment" RazorCode="@example4RazorCode" Id="example4"> + <ExamplePreview> + <div class="box"> + <BitMessage Alignment="BitAlignment.Start" Color="BitColor.Primary">Start</BitMessage> + <BitMessage Alignment="BitAlignment.Center" Color="BitColor.Secondary">Center</BitMessage> + <BitMessage Alignment="BitAlignment.End" Color="BitColor.Tertiary">End</BitMessage> + </div> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Elevation" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> + <ExamplePreview> + <div class="box"> + <BitMessage Elevation="(int)elevation">Elevated Message</BitMessage> + + <br /> + <BitSlider Label="Elevation" Min="0" Max="24" Step="1" @bind-Value="elevation" /> + </div> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Multiline" RazorCode="@example6RazorCode" Id="example6"> <ExamplePreview> <BitMessage Multiline Color="BitColor.Success"> <b>Multiline</b> parameter makes the content to be rendered in multiple lines. @@ -80,7 +116,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Truncate" RazorCode="@example5RazorCode" Id="example5"> + <ComponentExampleBox Title="Truncate" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> <BitMessage Truncate Color="BitColor.Warning"> <b>Truncate</b> parameter cut the overflowed content at the end of the single line Message. @@ -90,7 +126,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Dismiss" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="Dismiss" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> @if (isDismissed is false) { @@ -105,7 +141,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Actions" RazorCode="@example7RazorCode" Id="example7"> + <ComponentExampleBox Title="Actions" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> <BitMessage> <Actions> @@ -120,7 +156,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="HideIcon" RazorCode="@example8RazorCode" Id="example8"> + <ComponentExampleBox Title="HideIcon" RazorCode="@example10RazorCode" Id="example10"> <ExamplePreview> <div class="box"> <div>HideIcon parameter removes the icon:</div> @@ -133,19 +169,19 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Icons" RazorCode="@example9RazorCode" Id="example9"> + <ComponentExampleBox Title="Icons" RazorCode="@example11RazorCode" Id="example11"> <ExamplePreview> <BitMessage Color="BitColor.Success" IconName="@BitIconName.CheckMark"> Message with a custom icon. </BitMessage> <br /> - <BitMessage Color="BitColor.Warning" OnDismiss="() => {}" DismissIconName="@BitIconName.Blocked2Solid"> + <BitMessage Color="BitColor.Warning" OnDismiss="() => {}" DismissIcon="@BitIconName.Blocked2Solid"> Message with a custom dismiss icon. </BitMessage> <br /> <BitMessage Truncate Color="BitColor.Warning" - ExpandIconName="@BitIconName.ChevronDownEnd" - CollapseIconName="@BitIconName.ChevronUpEnd"> + ExpandIcon="@BitIconName.ChevronDownEnd" + CollapseIcon="@BitIconName.ChevronUpEnd"> Message with custom expand and collapse icon. Truncation is not available if you use multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. @@ -153,7 +189,61 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example10RazorCode" Id="example10"> + <ComponentExampleBox Title="Advanced" RazorCode="@example12RazorCode" CsharpCode="@example12CsharpCode" Id="example12"> + <ExamplePreview> + @if (isWarningDismissed is false) + { + <BitMessage Truncate OnDismiss="() => isWarningDismissed = true" Color="BitColor.Warning"> + <Content> + <b>Truncate</b> with <b>OnDismiss</b> and <b>Actions</b>. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. + </Content> + <Actions> + <div style="display:flex;align-items:center;gap:4px;min-height:32px"> + <button>Yes</button> + <button>No</button> + </div> + </Actions> + </BitMessage> + } + else + { + <BitButton OnClick="() => isWarningDismissed = false">Dismissed, click to reset</BitButton> + <br /> + } + <br /> + @if (isErrorDismissed is false) + { + <BitMessage Multiline OnDismiss="() => isErrorDismissed = true" Color="BitColor.Error"> + <Content> + <b>Multiline</b> with <b>OnDismiss</b> and <b>Actions</b>. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. + </Content> + <Actions> + <BitButton Variant="BitVariant.Outline">Yes</BitButton> + + <BitButton Variant="BitVariant.Outline">No</BitButton> + </Actions> + </BitMessage> + } + else + { + <BitButton OnClick="() => isErrorDismissed = false">Dismissed, click to reset</BitButton> + } + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Size" RazorCode="@example13RazorCode" Id="example13"> + <ExamplePreview> + <div class="box"> + <BitMessage Size="BitSize.Small" Color="BitColor.Primary">Small</BitMessage> + <BitMessage Size="BitSize.Medium" Color="BitColor.Secondary">Medium</BitMessage> + <BitMessage Size="BitSize.Large" Color="BitColor.Tertiary">Large</BitMessage> + </div> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Style & Class" RazorCode="@example14RazorCode" Id="example14"> <ExamplePreview> <div> <div>Component's Style & Class:</div> @@ -176,7 +266,7 @@ <BitMessage Color="BitColor.Warning" OnDismiss="() => {}" Multiline Styles="@(new() { Root="padding:1rem", IconContainer="line-height:1.25", - Content="color:pink", + Content="color:blueviolet", ContentContainer="margin:0 10px", DismissIcon="font-size:1rem", Actions="justify-content:center;gap:1rem" })"> @@ -202,51 +292,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Advanced" RazorCode="@example11RazorCode" CsharpCode="@example11CsharpCode" Id="example11"> - <ExamplePreview> - @if (isWarningDismissed is false) - { - <BitMessage Truncate OnDismiss="() => isWarningDismissed = true" Color="BitColor.Warning"> - <Content> - <b>Truncate</b> with <b>OnDismiss</b> and <b>Actions</b>. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. - </Content> - <Actions> - <div style="display:flex;align-items:center;gap:4px;min-height:32px"> - <button>Yes</button> - <button>No</button> - </div> - </Actions> - </BitMessage> - } - else - { - <BitButton OnClick="() => isWarningDismissed = false">Dismissed, click to reset</BitButton> - <br /> - } - <br /> - @if (isErrorDismissed is false) - { - <BitMessage Multiline OnDismiss="() => isErrorDismissed = true" Color="BitColor.Error"> - <Content> - <b>Multiline</b> with <b>OnDismiss</b> and <b>Actions</b>. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. - </Content> - <Actions> - <BitButton Variant="BitVariant.Outline">Yes</BitButton> - - <BitButton Variant="BitVariant.Outline">No</BitButton> - </Actions> - </BitMessage> - } - else - { - <BitButton OnClick="() => isErrorDismissed = false">Dismissed, click to reset</BitButton> - } - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="RTL" RazorCode="@example12RazorCode" Id="example12"> + <ComponentExampleBox Title="RTL" RazorCode="@example15RazorCode" Id="example15"> <ExamplePreview> <div class="box"> <BitMessage Dir="BitDir.Rtl" Color="BitColor.Info"> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.cs index 378536290c..d841de31cd 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.cs @@ -12,6 +12,15 @@ public partial class BitMessageDemo Description = "The content of the action to show on the message.", }, new() + { + Name = "Alignment", + Type = "BitAlignment?", + DefaultValue = "null", + Description = "Determines the alignment of the content section of the message.", + LinkType = LinkType.Link, + Href = "#alignment-enum", + }, + new() { Name = "ChildContent", Type = "RenderFragment?", @@ -29,7 +38,7 @@ public partial class BitMessageDemo }, new() { - Name = "CollapseIconName", + Name = "CollapseIcon", Type = "string", DefaultValue = "DoubleChevronUp", Description = "Custom Fabric icon name for the collapse icon in Truncate mode. If unset, default will be the Fabric DoubleChevronUp icon.", @@ -52,14 +61,21 @@ public partial class BitMessageDemo }, new() { - Name = "DismissIconName", + Name = "DismissIcon", Type = "string", DefaultValue = "Cancel", Description = "Custom Fabric icon name to replace the dismiss icon. If unset, default will be the Fabric Cancel icon.", }, new() { - Name = "ExpandIconName", + Name = "Elevation", + Type = "int?", + DefaultValue = "null", + Description = "Determines the elevation of the message, a scale from 1 to 24.", + }, + new() + { + Name = "ExpandIcon", Type = "string", DefaultValue = "DoubleChevronDown", Description = "Custom Fabric icon name for the expand icon in Truncate mode. If unset, default will be the Fabric DoubleChevronDown icon.", @@ -99,6 +115,15 @@ public partial class BitMessageDemo Description = "Custom role to apply to the message text.", }, new() + { + Name = "Size", + Type = "BitSize?", + DefaultValue = "null", + Description = "The size of Message, Possible values: Small | Medium | Large.", + LinkType = LinkType.Link, + Href = "#size-enum", + }, + new() { Name = "Styles", Type = "BitMessageClassStyles?", @@ -182,6 +207,60 @@ public partial class BitMessageDemo Description = "Error general color.", Value = "7", }, + new() + { + Name= "PrimaryBackground", + Description="Primary background color.", + Value="8", + }, + new() + { + Name= "SecondaryBackground", + Description="Secondary background color.", + Value="9", + }, + new() + { + Name= "TertiaryBackground", + Description="Tertiary background color.", + Value="10", + }, + new() + { + Name= "PrimaryForeground", + Description="Primary foreground color.", + Value="11", + }, + new() + { + Name= "SecondaryForeground", + Description="Secondary foreground color.", + Value="12", + }, + new() + { + Name= "TertiaryForeground", + Description="Tertiary foreground color.", + Value="13", + }, + new() + { + Name= "PrimaryBorder", + Description="Primary border color.", + Value="14", + }, + new() + { + Name= "SecondaryBorder", + Description="Secondary border color.", + Value="15", + }, + new() + { + Name= "TertiaryBorder", + Description="Tertiary border color.", + Value="16", + } ] }, new() @@ -210,7 +289,83 @@ public partial class BitMessageDemo Value = "2", }, ] - } + }, + new() + { + Id = "alignment-enum", + Name = "BitAlignment", + Description = "", + Items = + [ + new() + { + Name = "Start", + Value = "0", + }, + new() + { + Name = "End", + Value = "1", + }, + new() + { + Name = "Center", + Value = "2", + }, + new() + { + Name = "SpaceBetween", + Value = "3", + }, + new() + { + Name = "SpaceAround", + Value = "4", + }, + new() + { + Name = "SpaceEvenly", + Value = "5", + }, + new() + { + Name = "Baseline", + Value = "6", + }, + new() + { + Name = "Stretch", + Value = "7", + } + ] + }, + new() + { + Id = "size-enum", + Name = "BitSize", + Description = "", + Items = + [ + new() + { + Name= "Small", + Description="The small size message.", + Value="0", + }, + new() + { + Name= "Medium", + Description="The medium size message.", + Value="1", + }, + new() + { + Name= "Large", + Description="The large size message.", + Value="2", + } + ] + }, ]; private readonly List<ComponentSubClass> componentSubClasses = @@ -229,11 +384,18 @@ public partial class BitMessageDemo Description = "Custom CSS classes/styles for the root element of the BitMessage." }, new() + { + Name = "RootContainer", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the root container of the BitMessage." + }, + new() { Name = "Container", Type = "string?", DefaultValue = "null", - Description = "Custom CSS classes/styles for the main container of the BitMessage." + Description = "Custom CSS classes/styles for the icon and content container of the BitMessage." }, new() { @@ -312,6 +474,7 @@ public partial class BitMessageDemo private bool isDismissed; - private bool isWarningDismissed; + private double elevation = 7; private bool isErrorDismissed; + private bool isWarningDismissed; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.samples.cs index da50398997..daf9e7a55e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Message/BitMessageDemo.razor.samples.cs @@ -13,7 +13,18 @@ public partial class BitMessageDemo <BitMessage Color=""BitColor.Success"">Success.</BitMessage> <BitMessage Color=""BitColor.Warning"">Warning.</BitMessage> <BitMessage Color=""BitColor.SevereWarning"">SevereWarning.</BitMessage> -<BitMessage Color=""BitColor.Error"">Error.</BitMessage>"; +<BitMessage Color=""BitColor.Error"">Error.</BitMessage> + +<BitMessage Color=""BitColor.PrimaryBackground"">PrimaryBackground.</BitMessage> +<BitMessage Color=""BitColor.SecondaryBackground"">SecondaryBackground.</BitMessage> +<BitMessage Color=""BitColor.TertiaryBackground"">TertiaryBackground.</BitMessage> + +<BitMessage Color=""BitColor.PrimaryForeground"">PrimaryForeground.</BitMessage> +<BitMessage Color=""BitColor.SecondaryForeground"">SecondaryForeground.</BitMessage> +<BitMessage Color=""BitColor.TertiaryForeground"">TertiaryForeground.</BitMessage> +<BitMessage Color=""BitColor.PrimaryBorder"">PrimaryBorder.</BitMessage> +<BitMessage Color=""BitColor.SecondaryBorder"">SecondaryBorder.</BitMessage> +<BitMessage Color=""BitColor.TertiaryBorder"">TertiaryBorder.</BitMessage>"; private readonly string example3RazorCode = @" <BitMessage Color=""BitColor.Primary"" Variant=""BitVariant.Fill"">Primary.</BitMessage> @@ -44,26 +55,45 @@ public partial class BitMessageDemo <BitMessage Color=""BitColor.Error"" Variant=""BitVariant.Text"">Error.</BitMessage>"; private readonly string example4RazorCode = @" +<BitMessage Alignment=""BitAlignment.Start"" Color=""BitColor.Primary"">Start</BitMessage> +<BitMessage Alignment=""BitAlignment.Center"" Color=""BitColor.Secondary"">Center</BitMessage> +<BitMessage Alignment=""BitAlignment.End"" Color=""BitColor.Tertiary"">End</BitMessage>"; + + private readonly string example5RazorCode = @" +<BitMessage Elevation=""(int)elevation"">Elevated Message</BitMessage> + +<BitSlider Label=""Elevation"" Min=""0"" Max=""24"" Step=""1"" @bind-Value=""elevation"" />"; + private readonly string example5CsharpCode = @" +private double elevation = 7;"; + + private readonly string example6RazorCode = @" <BitMessage Multiline Color=""BitColor.Success""> <b>Multiline</b> parameter makes the content to be rendered in multiple lines. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. </BitMessage>"; - private readonly string example5RazorCode = @" + private readonly string example7RazorCode = @" <BitMessage Truncate Color=""BitColor.Warning""> <b>Truncate</b> parameter cut the overflowed content at the end of the single line Message. Truncation is not available if you use multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. </BitMessage>"; - private readonly string example6RazorCode = @" -<BitMessage OnDismiss=""() => isDismissed = true"" Color=""BitColor.SevereWarning""> - Dismiss option enabled by adding <strong>OnDismiss</strong> parameter. -</BitMessage>"; - private readonly string example6CsharpCode = @" + private readonly string example8RazorCode = @" +@if (isDismissed is false) +{ + <BitMessage OnDismiss=""() => isDismissed = true"" Color=""BitColor.SevereWarning""> + Dismiss option enabled by adding <strong>OnDismiss</strong> parameter. + </BitMessage> +} +else +{ + <BitButton OnClick=""() => isDismissed = false"">Dismissed, click to reset</BitButton> +}"; + private readonly string example8CsharpCode = @" private bool isDismissed;"; - private readonly string example7RazorCode = @" + private readonly string example9RazorCode = @" <BitMessage> <Actions> <BitButton Variant=""BitVariant.Text"" Color=""BitColor.Tertiary"" IconName=""@BitIconName.Up"" /> @@ -75,31 +105,65 @@ Message with single line and action buttons. </Content> </BitMessage>"; - private readonly string example8RazorCode = @" + private readonly string example10RazorCode = @" <BitMessage Color=""BitColor.Info"" HideIcon>Info (default) Message.</BitMessage> <BitMessage Color=""BitColor.Success"" HideIcon>Success Message.</BitMessage> <BitMessage Color=""BitColor.Warning"" HideIcon>Warning Message.</BitMessage> <BitMessage Color=""BitColor.SevereWarning"" HideIcon>SevereWarning Message.</BitMessage> <BitMessage Color=""BitColor.Error"" HideIcon>Error Message.</BitMessage>"; - private readonly string example9RazorCode = @" + private readonly string example11RazorCode = @" <BitMessage Color=""BitColor.Success"" IconName=""@BitIconName.CheckMark""> Message with a custom icon. </BitMessage> -<BitMessage Color=""BitColor.Warning"" OnDismiss=""() => {}"" DismissIconName=""@BitIconName.Blocked2Solid""> +<BitMessage Color=""BitColor.Warning"" OnDismiss=""() => {}"" DismissIcon=""@BitIconName.Blocked2Solid""> Message with a custom dismiss icon. </BitMessage> <BitMessage Truncate Color=""BitColor.Warning"" - ExpandIconName=""@BitIconName.ChevronDownEnd"" - CollapseIconName=""@BitIconName.ChevronUpEnd""> + ExpandIcon=""@BitIconName.ChevronDownEnd"" + CollapseIcon=""@BitIconName.ChevronUpEnd""> Message with custom expand and collapse icon. Truncation is not available if you use multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. </BitMessage>"; - private readonly string example10RazorCode = @" + private readonly string example12RazorCode = @" +<BitMessage Truncate OnDismiss=""() => isWarningDismissed = true"" Color=""BitColor.Warning""> + <Content> + <b>Truncate</b> with <b>OnDismiss</b> and <b>Actions</b>. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. + </Content> + <Actions> + <div style=""display:flex;align-items:center;gap:4px""> + <button>Yes</button> + <button>No</button> + </div> + </Actions> +</BitMessage> + +<BitMessage Multiline OnDismiss=""() => isErrorDismissed = true"" Color=""BitColor.Error""> + <Content> + <b>Multiline</b> with <b>OnDismiss</b> and <b>Actions</b>. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. + </Content> + <Actions> + <BitButton Variant=""BitVariant.Outline"">Yes</BitButton> + + <BitButton Variant=""BitVariant.Outline"">No</BitButton> + </Actions> +</BitMessage>"; + private readonly string example12CsharpCode = @" +private bool isWarningDismissed; +private bool isErrorDismissed;"; + + private readonly string example13RazorCode = @" +<BitMessage Size=""BitSize.Small"" Color=""BitColor.Primary"">Small</BitMessage> +<BitMessage Size=""BitSize.Medium"" Color=""BitColor.Secondary"">Medium</BitMessage> +<BitMessage Size=""BitSize.Large"" Color=""BitColor.Tertiary"">Large</BitMessage>"; + + private readonly string example14RazorCode = @" <style> .custom-class { padding: 1rem; @@ -142,7 +206,7 @@ Message with custom expand and collapse icon. <BitMessage Color=""BitColor.Warning"" OnDismiss=""() => {}"" Multiline Styles=""@(new() { Root=""padding:1rem"", IconContainer=""line-height:1.25"", - Content=""color:pink"", + Content=""color:blueviolet"", ContentContainer=""margin:0 10px"", DismissIcon=""font-size:1rem"", Actions=""justify-content:center;gap:1rem"" })""> @@ -165,36 +229,7 @@ Message with custom expand and collapse icon. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. </BitMessage>"; - private readonly string example11RazorCode = @" -<BitMessage Truncate OnDismiss=""() => isWarningDismissed = true"" Color=""BitColor.Warning""> - <Content> - <b>Truncate</b> with <b>OnDismiss</b> and <b>Actions</b>. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. - </Content> - <Actions> - <div style=""display:flex;align-items:center;gap:4px""> - <button>Yes</button> - <button>No</button> - </div> - </Actions> -</BitMessage> - -<BitMessage Multiline OnDismiss=""() => isErrorDismissed = true"" Color=""BitColor.Error""> - <Content> - <b>Multiline</b> with <b>OnDismiss</b> and <b>Actions</b>. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris. - </Content> - <Actions> - <BitButton Variant=""BitVariant.Outline"">Yes</BitButton> - - <BitButton Variant=""BitVariant.Outline"">No</BitButton> - </Actions> -</BitMessage>"; - private readonly string example11CsharpCode = @" -private bool isWarningDismissed; -private bool isErrorDismissed;"; - - private readonly string example12RazorCode = @" + private readonly string example15RazorCode = @" <BitMessage Dir=""BitDir.Rtl"" Color=""BitColor.Info""> پیام خبری (پیش فرض). <BitLink Href=""https://bitplatform.dev"">به وبسایت ما سر بزنید.</BitLink> </BitMessage> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor index 5f1cb9b784..cb50a176f2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor @@ -10,19 +10,15 @@ ComponentSubEnums="componentSubEnums" ComponentSubClasses="componentSubClasses"> - <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <BitPersona PrimaryText="Saleh Khafan" Size="BitPersonaSize.Size72" /> - + <BitPersona PrimaryText="Saleh Khafan" + Size="BitPersonaSize.Size72" /> <br /><br /> - <BitPersona PrimaryText="Annie Lindqvist" Size="BitPersonaSize.Size72" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - <br /><br /> - <BitPersona PrimaryText="Unknown" SecondaryText="Developer" Size="BitPersonaSize.Size72" @@ -30,122 +26,171 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Color" RazorCode="@example2RazorCode" Id="example2"> - <ExamplePreview> - <BitPersona PrimaryText="Saleh Khafan" - SecondaryText="Developer" - Size="BitPersonaSize.Size72" - Color="#038387" /> - - - <br /><br /> - - <BitPersona PrimaryText="Annie Lindqvist" - Size="BitPersonaSize.Size72" - ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" - OnImageClick="() => {}" - Color="#750b1c" /> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Size" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> + <ComponentExampleBox Title="Size" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> <BitCheckbox @bind-Value="isDetailsShown" Label="Include BitPersona details" /> - - <div class="title">Size 8 Persona</div> + <br /><br /><br /><br /> + <div>Size 8 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Secondary" Size="BitPersonaSize.Size8" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Online" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 24 Persona</div> + <br /><br /><br /><br /> + <div>Size 24 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Secondary" Size="BitPersonaSize.Size24" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.None" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 32 Persona (Busy)</div> + <br /><br /><br /><br /> + <div>Size 32 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Secondary" Size=@BitPersonaSize.Size32 HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Busy" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 40 Persona (Away)</div> + <br /><br /><br /><br /> + <div>Size 40 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Size="BitPersonaSize.Size40" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Away" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 48 Persona (Blocked)</div> + <br /><br /><br /><br /> + <div>Size 48 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Size="BitPersonaSize.Size48" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Blocked" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 56 Persona (Online)</div> + <br /><br /><br /><br /> + <div>Size 56 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Size="BitPersonaSize.Size56" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Online" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 72 Persona (Busy)</div> + <br /><br /><br /><br /> + <div>Size 72 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="In a meeting" Size="BitPersonaSize.Size72" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Busy" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 100 Persona (Offline)</div> + <br /><br /><br /><br /> + <div>Size 100 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="Off" OptionalText="Available at 4:00pm" Size="BitPersonaSize.Size100" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Offline" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 120 Persona (Do Not Disturb)</div> + <br /><br /><br /><br /> + <div>Size 120 Persona:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="In a meeting" OptionalText="Available at 4:00pm" Size="BitPersonaSize.Size120" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Dnd" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Size 150 Persona (Do Not Disturb)</div> - <BitPersona PrimaryText="Annie Lindqvist" + <br /><br /><br /><br /> + <div>CoinSize 150:</div><br /> + <BitPersona CoinSize="150" + PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="In a meeting" OptionalText="Available at 4:00pm" Size="BitPersonaSize.Size120" - CoinSize="150" HidePersonaDetails="!isDetailsShown" - Presence="BitPersonaPresence.Dnd" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Action" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ComponentExampleBox Title="Color" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> - <div>Custom action can be revealed by hovering on the bottom of the image</div> - <br /> + <BitPersona PrimaryText="Primary" CoinColor="BitColor.Primary" /> + <br /><br /> + <BitPersona PrimaryText="Secondary" CoinColor="BitColor.Secondary" /> + <br /><br /> + <BitPersona PrimaryText="Tertiary" CoinColor="BitColor.Tertiary" /> + <br /><br /> + <BitPersona PrimaryText="Info" SecondaryText="(default)" CoinColor="BitColor.Info" /> + <br /><br /> + <BitPersona PrimaryText="Success" CoinColor="BitColor.Success" /> + <br /><br /> + <BitPersona PrimaryText="Warning" CoinColor="BitColor.Warning" /> + <br /><br /> + <BitPersona PrimaryText="SevereWarning" CoinColor="BitColor.SevereWarning" /> + <br /><br /> + <BitPersona PrimaryText="Error" CoinColor="BitColor.Error" /> + <br /><br /> + <div style="background:var(--bit-clr-fg-ter);padding:1rem;margin: 1rem 0;"> + <div class="box"> + <BitPersona PrimaryText="PrimaryBackground" CoinColor="BitColor.PrimaryBackground" /> + <br /><br /> + <BitPersona PrimaryText="SecondaryBackground" CoinColor="BitColor.SecondaryBackground" /> + <br /><br /> + <BitPersona PrimaryText="TertiaryBackground" CoinColor="BitColor.TertiaryBackground" /> + </div> + </div> + + <br /><br /> + <BitPersona PrimaryText="PrimaryForeground" CoinColor="BitColor.PrimaryForeground" /> + <br /><br /> + <BitPersona PrimaryText="SecondaryForeground" CoinColor="BitColor.SecondaryForeground" /> + <br /><br /> + <BitPersona PrimaryText="TertiaryForeground" CoinColor="BitColor.TertiaryForeground" /> + <br /><br /> + <BitPersona PrimaryText="PrimaryBorder" CoinColor="BitColor.PrimaryBorder" /> + <br /><br /> + <BitPersona PrimaryText="SecondaryBorder" CoinColor="BitColor.SecondaryBorder" /> + <br /><br /> + <BitPersona PrimaryText="TertiaryBorder" CoinColor="BitColor.TertiaryBorder" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Variant" RazorCode="@example4RazorCode" Id="example4"> + <ExamplePreview> + <BitPersona PrimaryText="Saleh Xafan" + SecondaryText="Developer" + Size="BitPersonaSize.Size72" + CoinVariant="BitVariant.Fill" /> + <br /><br /> + <BitPersona PrimaryText="Saleh Xafan" + SecondaryText="Developer" + Size="BitPersonaSize.Size72" + CoinVariant="BitVariant.Outline" /> + <br /><br /> + <BitPersona PrimaryText="Saleh Xafan" + SecondaryText="Developer" + Size="BitPersonaSize.Size72" + CoinVariant="BitVariant.Text" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Shape" RazorCode="@example5RazorCode" Id="example5"> + <ExamplePreview> + <BitPersona PrimaryText="Saleh Xafan" + SecondaryText="Developer" + Size="BitPersonaSize.Size72" + CoinShape="BitPersonaCoinShape.Circular" + ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> + <br /><br /> + <BitPersona PrimaryText="Saleh Xafan" + SecondaryText="Developer" + Size="BitPersonaSize.Size72" + CoinShape="BitPersonaCoinShape.Square" + ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Action" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ExamplePreview> + <div>Custom action can be revealed by hovering on the bottom of the image:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="In a meeting" @@ -156,10 +201,8 @@ ActionIconName="@BitIconName.CloudUpload" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> <p>Action Click Count: @actionClickCount</p> - <br /><br /> - - <div>Hover over the image to reveal the action</div> - <br /> + <br /><br /><br /><br /> + <div>Hover over the image to reveal the image click action:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" TertiaryText="In a meeting" @@ -172,72 +215,72 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Initials" RazorCode="@example5RazorCode" Id="example5"> + <ComponentExampleBox Title="Initials" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> - <div class="title">Invalid ImageUrl</div> + <div>Invalid ImageUrl:</div><br /> <BitPersona PrimaryText="Saleh Khafan" Size="BitPersonaSize.Size72" ShowInitialsUntilImageLoads ImageUrl="invalid-src" /> - - <div class="title">No ImageUrl</div> + <br /><br /><br /><br /> + <div>No ImageUrl:</div><br /> <BitPersona Size="BitPersonaSize.Size72" PrimaryText="Saleh Xafan" /> - - <div class="title">ImageInitials</div> + <br /><br /><br /><br /> + <div>ImageInitials:</div><br /> <BitPersona Size="BitPersonaSize.Size72" PrimaryText="Saleh Khafan" ImageInitials="S" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="PersonaPresence" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="PersonaPresence" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> - <div class="title">None</div> + <div>None:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.None" PresenceIcons="_icons" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Offline</div> + <br /><br /><br /><br /> + <div>Offline:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Offline" PresenceIcons="_icons" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Online</div> + <br /><br /><br /><br /> + <div>Online:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Online" PresenceIcons="_icons" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Away</div> + <br /><br /><br /><br /> + <div>Away:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Away" PresenceIcons="_icons" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Do not Disturb (Dnd)</div> + <br /><br /><br /><br /> + <div>Do not Disturb (Dnd):</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Dnd" PresenceIcons="_icons" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Blocked</div> + <br /><br /><br /><br /> + <div>Blocked:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Blocked" Size="BitPersonaSize.Size120" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" /> - - <div class="title">Busy</div> + <br /><br /><br /><br /> + <div>Busy:</div><br /> <BitPersona PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Busy" @@ -247,9 +290,9 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Templates" RazorCode="@example7RazorCode" Id="example7"> + <ComponentExampleBox Title="Templates" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> - <div class="title">Text templates</div> + <div>Text templates:</div><br /> <div> <BitPersona Size="BitPersonaSize.Size100" ImageUrl="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" OnImageClick="() => {}"> <PrimaryTextTemplate> @@ -274,10 +317,10 @@ </ImageOverlayTemplate> </BitPersona> </div> - - <div class="title">Coin template</div> + <br /><br /><br /><br /> + <div>Coin template:</div><br /> <div> - <BitPersona Size="BitPersonaSize.Size100" PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Online"> + <BitPersona Size="BitPersonaSize.Size100" PrimaryText="Annie Lindqvist" SecondaryText="Software Engineer" Presence="BitPersonaPresence.Online" CoinVariant="BitVariant.Text"> <CoinTemplate> <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png" width="100px" height="100px" class="custom-coin" /> </CoinTemplate> @@ -286,44 +329,38 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example8RazorCode" Id="example8"> + <ComponentExampleBox Title="Style & Class" RazorCode="@example10RazorCode" Id="example10"> <ExamplePreview> - <div>Component's Style & Class:</div> - <br /> - <div class="example-content"> - <BitPersona PrimaryText="Saleh Khafan" - Size="BitPersonaSize.Size72" - Style="padding: 1rem; background: gray;border-radius: 1rem;" /> - - <br /> - <BitPersona PrimaryText="Saleh Khafan" - Size="BitPersonaSize.Size72" - Class="custom-class" /> - </div> - <br /><br /><br /> - <div><b>Styles</b> & <b>Classes</b>:</div> - <br /> - <div class="example-content"> - <BitPersona PrimaryText="Saleh Khafan" - Size="BitPersonaSize.Size72" - Styles="@(new() { ImageContainer = "color: #b6ff00; background-color: #00ff90;", + <div>Component's Style & Class:</div><br /> + <BitPersona PrimaryText="Saleh Khafan" + Size="BitPersonaSize.Size72" + Style="padding: 1rem; background: gray;border-radius: 1rem;" /> + <br /><br /> + <BitPersona PrimaryText="Saleh Khafan" + Size="BitPersonaSize.Size72" + Class="custom-class" /> + <br /><br /><br /><br /> + <div><b>Styles</b> & <b>Classes</b>:</div><br /> + <BitPersona PrimaryText="Saleh Khafan" + Size="BitPersonaSize.Size72" + Styles="@(new() { ImageContainer = "color: #b6ff00; background-color: #00ff90;", PrimaryTextContainer = "color: #ea1919; font-weight: bold; font-style: italic;" })" /> - - <br /> - <BitPersona PrimaryText="Saleh Khafan" - Size="BitPersonaSize.Size72" - Classes="@(new() { ImageContainer = "custom-img-container", + <br /><br /> + <BitPersona PrimaryText="Saleh Khafan" + Size="BitPersonaSize.Size72" + Classes="@(new() { ImageContainer = "custom-img-container", PrimaryTextContainer = "custom-primary-text" })" /> - </div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" Id="example9"> + <ComponentExampleBox Title="RTL" RazorCode="@example11RazorCode" Id="example11"> <ExamplePreview> - <BitPersona Dir="BitDir.Rtl" - PrimaryText="صالح یوسف نژاد" - SecondaryText="مهندس نرم افزار" - Size="@BitPersonaSize.Size56" /> + <div dir="rtl"> + <BitPersona Dir="BitDir.Rtl" + PrimaryText="صالح یوسف نژاد" + SecondaryText="مهندس نرم افزار" + Size="@BitPersonaSize.Size56" /> + </div> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.cs index 0c4f58fd37..26d1a40f93 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.cs @@ -35,6 +35,24 @@ public partial class BitPersonaDemo LinkType = LinkType.Link }, new() + { + Name = "CoinColor", + Type = "BitColor?", + DefaultValue = "null", + Description = "The background color when the user's initials are displayed.", + LinkType = LinkType.Link, + Href = "#color-enum", + }, + new() + { + Name = "CoinShape", + Type = "BitPersonaCoinShape?", + DefaultValue = "null", + Description = "The shape of the coin.", + LinkType = LinkType.Link, + Href = "#shape-enum", + }, + new() { Name = "CoinSize", Type = "int?", @@ -50,10 +68,12 @@ public partial class BitPersonaDemo }, new() { - Name = "Color", - Type = "string?", + Name = "CoinVariant", + Type = "BitVariant?", DefaultValue = "null", - Description = "The background color when the user's initials are displayed.", + Description = "The variant of the coin.", + LinkType = LinkType.Link, + Href = "#variant-enum", }, new() { @@ -130,7 +150,7 @@ public partial class BitPersonaDemo Name = "Presence", Type = "BitPersonaPresence", LinkType = LinkType.Link, - Href = "#precence-status", + Href = "#presence-enum", DefaultValue = "BitPersonaPresence.None", Description = "Presence of the person to display - will not display presence if undefined.", }, @@ -196,7 +216,7 @@ public partial class BitPersonaDemo Type = "string?", DefaultValue = "null", LinkType = LinkType.Link, - Href = "#bitpersona-size", + Href = "#size-enum", Description = "Decides the size of the control.", }, new() @@ -366,7 +386,7 @@ public partial class BitPersonaDemo [ new() { - Id = "precence-status", + Id = "presence-enum", Name = "BitPersonaPresence", Items = [ @@ -409,7 +429,7 @@ public partial class BitPersonaDemo }, new() { - Id = "bitpersona-size", + Id = "size-enum", Name = "BitPersonaSize", Items = [ @@ -469,6 +489,164 @@ public partial class BitPersonaDemo } ] }, + new() + { + Id = "shape-enum", + Name = "BitPersonaCoinShape", + Items = + [ + new() + { + Name = "Circular", + Description = "Represents the traditional round shape of a coin.", + Value = "", + }, + new() + { + Name = "Square", + Description = "Represents a square-shaped coin.", + Value = "", + } + ] + }, + new() + { + Id = "color-enum", + Name = "BitColor", + Description = "Defines the general colors available in the bit BlazorUI.", + Items = + [ + new() + { + Name = "Primary", + Description = "Primary general color.", + Value = "0", + }, + new() + { + Name = "Secondary", + Description = "Secondary general color.", + Value = "1", + }, + new() + { + Name = "Tertiary", + Description = "Tertiary general color.", + Value = "2", + }, + new() + { + Name = "Info", + Description = "Info general color.", + Value = "3", + }, + new() + { + Name = "Success", + Description = "Success general color.", + Value = "4", + }, + new() + { + Name = "Warning", + Description = "Warning general color.", + Value = "5", + }, + new() + { + Name = "SevereWarning", + Description = "SevereWarning general color.", + Value = "6", + }, + new() + { + Name = "Error", + Description = "Error general color.", + Value = "7", + }, + new() + { + Name= "PrimaryBackground", + Description="Primary background color.", + Value="8", + }, + new() + { + Name= "SecondaryBackground", + Description="Secondary background color.", + Value="9", + }, + new() + { + Name= "TertiaryBackground", + Description="Tertiary background color.", + Value="10", + }, + new() + { + Name= "PrimaryForeground", + Description="Primary foreground color.", + Value="11", + }, + new() + { + Name= "SecondaryForeground", + Description="Secondary foreground color.", + Value="12", + }, + new() + { + Name= "TertiaryForeground", + Description="Tertiary foreground color.", + Value="13", + }, + new() + { + Name= "PrimaryBorder", + Description="Primary border color.", + Value="14", + }, + new() + { + Name= "SecondaryBorder", + Description="Secondary border color.", + Value="15", + }, + new() + { + Name= "TertiaryBorder", + Description="Tertiary border color.", + Value="16", + } + ] + }, + new() + { + Id = "variant-enum", + Name = "BitVariant", + Description = "Determines the variant of the content that controls the rendered style of the corresponding element(s).", + Items = + [ + new() + { + Name = "Fill", + Description = "Fill styled variant.", + Value = "0", + }, + new() + { + Name = "Outline", + Description = "Outline styled variant.", + Value = "1", + }, + new() + { + Name = "Text", + Description = "Text styled variant.", + Value = "2", + }, + ] + }, ]; @@ -502,111 +680,129 @@ public partial class BitPersonaDemo Unknown />"; private readonly string example2RazorCode = @" -<BitPersona PrimaryText=""Saleh Khafan"" - SecondaryText=""Developer"" - Size=""BitPersonaSize.Size72"" - Color=""#038387"" /> - -<BitPersona PrimaryText=""Annie Lindqvist"" - Size=""BitPersonaSize.Size72"" - ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" - OnImageClick=""() => {}"" - Color=""#750b1c"" />"; - - private readonly string example3RazorCode = @" <BitCheckbox @bind-Value=""isDetailsShown"" Label=""Include BitPersona details"" /> -<div>Size 8 Persona</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Secondary"" Size=""BitPersonaSize.Size8"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Online"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 24 Persona</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Secondary"" Size=""BitPersonaSize.Size24"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.None"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 32 Persona (Busy)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Secondary"" Size=@BitPersonaSize.Size32 HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Busy"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 40 Persona (Away)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" Size=""BitPersonaSize.Size40"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Away"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 48 Persona (Blocked)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" Size=""BitPersonaSize.Size48"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Blocked"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 56 Persona (Online)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" Size=""BitPersonaSize.Size56"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Online"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 72 Persona (Busy)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" TertiaryText=""In a meeting"" Size=""BitPersonaSize.Size72"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Busy"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 100 Persona (Offline)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" TertiaryText=""Off"" OptionalText=""Available at 4:00pm"" Size=""BitPersonaSize.Size100"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Offline"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 120 Persona (Do Not Disturb)</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" TertiaryText=""In a meeting"" OptionalText=""Available at 4:00pm"" Size=""BitPersonaSize.Size120"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Dnd"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> -<div>Size 150 Persona (Do Not Disturb)</div> -<BitPersona PrimaryText=""Annie Lindqvist"" +<BitPersona CoinSize=""150"" + PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" TertiaryText=""In a meeting"" OptionalText=""Available at 4:00pm"" Size=""BitPersonaSize.Size120"" - CoinSize=""150"" HidePersonaDetails=""!isDetailsShown"" - Presence=""BitPersonaPresence.Dnd"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" />"; - private readonly string example3CsharpCode = @" + private readonly string example2CsharpCode = @" private bool isDetailsShown = true;"; + private readonly string example3RazorCode = @" +<BitPersona PrimaryText=""Primary"" CoinColor=""BitColor.Primary"" /> +<BitPersona PrimaryText=""Secondary"" CoinColor=""BitColor.Secondary"" /> +<BitPersona PrimaryText=""Tertiary"" CoinColor=""BitColor.Tertiary"" /> +<BitPersona PrimaryText=""Info"" SecondaryText=""(default)"" CoinColor=""BitColor.Info"" /> +<BitPersona PrimaryText=""Success"" CoinColor=""BitColor.Success"" /> +<BitPersona PrimaryText=""Warning"" CoinColor=""BitColor.Warning"" /> +<BitPersona PrimaryText=""SevereWarning"" CoinColor=""BitColor.SevereWarning"" /> +<BitPersona PrimaryText=""Error"" CoinColor=""BitColor.Error"" /> + +<BitPersona PrimaryText=""PrimaryBackground"" CoinColor=""BitColor.PrimaryBackground"" /> +<BitPersona PrimaryText=""SecondaryBackground"" CoinColor=""BitColor.SecondaryBackground"" /> +<BitPersona PrimaryText=""TertiaryBackground"" CoinColor=""BitColor.TertiaryBackground"" /> + +<BitPersona PrimaryText=""PrimaryForeground"" CoinColor=""BitColor.PrimaryForeground"" /> +<BitPersona PrimaryText=""SecondaryForeground"" CoinColor=""BitColor.SecondaryForeground"" /> +<BitPersona PrimaryText=""TertiaryForeground"" CoinColor=""BitColor.TertiaryForeground"" /> +<BitPersona PrimaryText=""PrimaryBorder"" CoinColor=""BitColor.PrimaryBorder"" /> +<BitPersona PrimaryText=""SecondaryBorder"" CoinColor=""BitColor.SecondaryBorder"" /> +<BitPersona PrimaryText=""TertiaryBorder"" CoinColor=""BitColor.TertiaryBorder"" />"; + private readonly string example4RazorCode = @" +<BitPersona PrimaryText=""Saleh Xafan"" + SecondaryText=""Developer"" + Size=""BitPersonaSize.Size72"" + CoinVariant=""BitVariant.Fill"" /> + +<BitPersona PrimaryText=""Saleh Xafan"" + SecondaryText=""Developer"" + Size=""BitPersonaSize.Size72"" + CoinVariant=""BitVariant.Outline"" /> + +<BitPersona PrimaryText=""Saleh Xafan"" + SecondaryText=""Developer"" + Size=""BitPersonaSize.Size72"" + CoinVariant=""BitVariant.Text"" />"; + + private readonly string example5RazorCode = @" +<BitPersona PrimaryText=""Saleh Xafan"" + SecondaryText=""Developer"" + Size=""BitPersonaSize.Size72"" + CoinShape=""BitPersonaCoinShape.Circular"" + ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> + +<BitPersona PrimaryText=""Saleh Xafan"" + SecondaryText=""Developer"" + Size=""BitPersonaSize.Size72"" + CoinShape=""BitPersonaCoinShape.Square"" + ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" />"; + + private readonly string example6RazorCode = @" <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" TertiaryText=""In a meeting"" @@ -627,11 +823,11 @@ public partial class BitPersonaDemo OnImageClick=""() => imageClickCount++"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" /> <p>Image Click Count: @imageClickCount</p>"; - private readonly string example4CsharpCode = @" + private readonly string example6CsharpCode = @" private int imageClickCount = 0; private int actionClickCount = 0;"; - private readonly string example5RazorCode = @" + private readonly string example7RazorCode = @" <BitPersona PrimaryText=""Saleh Khafan"" Size=""BitPersonaSize.Size72"" ShowInitialsUntilImageLoads @@ -641,7 +837,7 @@ public partial class BitPersonaDemo <BitPersona Size=""BitPersonaSize.Size72"" PrimaryText=""Saleh Khafan"" ImageInitials=""S"" />"; - private readonly string example6RazorCode = @" + private readonly string example8RazorCode = @" <div>None</div> <BitPersona PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" @@ -696,7 +892,7 @@ public partial class BitPersonaDemo PresenceIcons=""_icons"" Size=""BitPersonaSize.Size120"" ImageUrl=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" />"; - private readonly string example6CsharpCode = @" + private readonly string example8CsharpCode = @" private Dictionary<BitPersonaPresence, string> _icons = new() { {BitPersonaPresence.Offline, BitIconName.UnavailableOffline}, @@ -707,7 +903,7 @@ public partial class BitPersonaDemo {BitPersonaPresence.Busy, BitIconName.Blocked2Solid} };"; - private readonly string example7RazorCode = @" + private readonly string example9RazorCode = @" <style> .custom-ico { font-size: 14px; @@ -744,13 +940,13 @@ Edit image </BitPersona> -<BitPersona Size=""BitPersonaSize.Size100"" PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" Presence=""BitPersonaPresence.Online""> +<BitPersona Size=""BitPersonaSize.Size100"" PrimaryText=""Annie Lindqvist"" SecondaryText=""Software Engineer"" Presence=""BitPersonaPresence.Online"" CoinVariant=""BitVariant.Text""> <CoinTemplate> <img src=""/_content/Bit.BlazorUI.Demo.Client.Core/images/persona/persona-female.png"" width=""100px"" height=""100px"" class=""custom-coin"" /> </CoinTemplate> </BitPersona>"; - private readonly string example8RazorCode = @" + private readonly string example10RazorCode = @" <style> .custom-class { padding: 1rem; @@ -789,7 +985,7 @@ Edit image Classes=""@(new() { ImageContainer = ""custom-img-container"", PrimaryTextContainer = ""custom-primary-text"" })"" />"; - private readonly string example9RazorCode = @" + private readonly string example11RazorCode = @" <BitPersona Dir=""BitDir.Rtl"" PrimaryText=""صالح یوسف نژاد"" SecondaryText=""مهندس نرم افزار"" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.scss index 64db762eb8..edf3c90f9f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Persona/BitPersonaDemo.razor.scss @@ -1,15 +1,5 @@ @import "../../../../Styles/abstracts/_functions.scss"; -.example-content { - max-width: rem2(300px); -} - -.title { - font-weight: 600; - margin-top: rem2(40px); - margin-bottom: rem2(20px); -} - ::deep { .custom-ico { font-size: rem2(14px); diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor index 8d0a71afc5..e422885f1b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor @@ -19,48 +19,64 @@ <ComponentExampleBox Title="Customization" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div class="example-box"> - <BitSnackBar @ref="customizationRef" - Dir="direction" - Position="@basicSnackBarPosition" - AutoDismiss="@basicSnackBarAutoDismiss" - AutoDismissTime="TimeSpan.FromSeconds(basicSnackBarDismissSeconds)" /> - - <BitChoiceGroup @bind-Value="basicSnackBarType" Label="Type" TItem="BitChoiceGroupOption<BitSnackBarType>" TValue="BitSnackBarType"> - <BitChoiceGroupOption Text="Info" Value="BitSnackBarType.Info" /> - <BitChoiceGroupOption Text="Success" Value="BitSnackBarType.Success" /> - <BitChoiceGroupOption Text="Warning" Value="BitSnackBarType.Warning" /> - <BitChoiceGroupOption Text="SevereWarning" Value="BitSnackBarType.SevereWarning" /> - <BitChoiceGroupOption Text="Error" Value="BitSnackBarType.Error" /> - </BitChoiceGroup> + <BitSnackBar @ref="customizationRef" + Dir="direction" + Position="basicSnackBarPosition" + Multiline="basicSnackBarMultiline" + AutoDismiss="basicSnackBarAutoDismiss" + AutoDismissTime="TimeSpan.FromSeconds(basicSnackBarDismissSeconds)" /> - <BitChoiceGroup @bind-Value="basicSnackBarPosition" Label="Position" TItem="BitChoiceGroupOption<BitSnackBarPosition>" TValue="BitSnackBarPosition"> - <BitChoiceGroupOption Text="TopLeft" Value="BitSnackBarPosition.TopLeft" /> - <BitChoiceGroupOption Text="TopCenter" Value="BitSnackBarPosition.TopCenter" /> - <BitChoiceGroupOption Text="TopRight" Value="BitSnackBarPosition.TopRight" /> - <BitChoiceGroupOption Text="BottomLeft" Value="BitSnackBarPosition.BottomLeft" /> - <BitChoiceGroupOption Text="BottomCenter" Value="BitSnackBarPosition.BottomCenter" /> - <BitChoiceGroupOption Text="BottomRight" Value="BitSnackBarPosition.BottomRight" /> - </BitChoiceGroup> - - <BitChoiceGroup @bind-Value="direction" Label="Direction" TItem="BitChoiceGroupOption<BitDir>" TValue="BitDir"> - <BitChoiceGroupOption Text="LTR" Value="BitDir.Ltr" /> - <BitChoiceGroupOption Text="RTL" Value="BitDir.Rtl" /> - <BitChoiceGroupOption Text="Auto" Value="BitDir.Auto" /> + <BitButton OnClick="OpenCustomizationSnackBar">Show</BitButton> + <br /><br /> + <div class="example-box"> + <BitChoiceGroup @bind-Value="basicSnackBarColor" Label="Color" TItem="BitChoiceGroupOption<BitColor>" TValue="BitColor"> + <BitChoiceGroupOption Text="Primary" Value="BitColor.Primary" /> + <BitChoiceGroupOption Text="Secondary" Value="BitColor.Secondary" /> + <BitChoiceGroupOption Text="Tertiary" Value="BitColor.Tertiary" /> + <BitChoiceGroupOption Text="Info" Value="BitColor.Info" /> + <BitChoiceGroupOption Text="Success" Value="BitColor.Success" /> + <BitChoiceGroupOption Text="Warning" Value="BitColor.Warning" /> + <BitChoiceGroupOption Text="SevereWarning" Value="BitColor.SevereWarning" /> + <BitChoiceGroupOption Text="Error" Value="BitColor.Error" /> + <BitChoiceGroupOption Text="PrimaryBackground" Value="BitColor.PrimaryBackground" /> + <BitChoiceGroupOption Text="SecondaryBackground" Value="BitColor.SecondaryBackground" /> + <BitChoiceGroupOption Text="TertiaryBackground" Value="BitColor.TertiaryBackground" /> + <BitChoiceGroupOption Text="PrimaryForeground" Value="BitColor.PrimaryForeground" /> + <BitChoiceGroupOption Text="SecondaryForeground" Value="BitColor.SecondaryForeground" /> + <BitChoiceGroupOption Text="TertiaryForeground" Value="BitColor.TertiaryForeground" /> + <BitChoiceGroupOption Text="PrimaryBorder" Value="BitColor.PrimaryBorder" /> + <BitChoiceGroupOption Text="SecondaryBorder" Value="BitColor.SecondaryBorder" /> + <BitChoiceGroupOption Text="TertiaryBorder" Value="BitColor.TertiaryBorder" /> </BitChoiceGroup> <div> - <BitTextField @bind-Value="basicSnackBarTitle" Label="Title" DefaultValue="Title" /> - <BitTextField @bind-Value="basicSnackBarBody" Label="Body" IsMultiline="true" Rows="6" DefaultValue="This is a body!" /> + <BitChoiceGroup @bind-Value="basicSnackBarPosition" Label="Position" TItem="BitChoiceGroupOption<BitSnackBarPosition>" TValue="BitSnackBarPosition"> + <BitChoiceGroupOption Text="TopStart" Value="BitSnackBarPosition.TopStart" /> + <BitChoiceGroupOption Text="TopCenter" Value="BitSnackBarPosition.TopCenter" /> + <BitChoiceGroupOption Text="TopEnd" Value="BitSnackBarPosition.TopEnd" /> + <BitChoiceGroupOption Text="BottomStart" Value="BitSnackBarPosition.BottomStart" /> + <BitChoiceGroupOption Text="BottomCenter" Value="BitSnackBarPosition.BottomCenter" /> + <BitChoiceGroupOption Text="BottomEnd" Value="BitSnackBarPosition.BottomEnd" /> + </BitChoiceGroup> + <br /> + <BitChoiceGroup @bind-Value="direction" Label="Direction" TItem="BitChoiceGroupOption<BitDir>" TValue="BitDir"> + <BitChoiceGroupOption Text="LTR" Value="BitDir.Ltr" /> + <BitChoiceGroupOption Text="RTL" Value="BitDir.Rtl" /> + <BitChoiceGroupOption Text="Auto" Value="BitDir.Auto" /> + </BitChoiceGroup> </div> <div> - <BitToggle @bind-Value="basicSnackBarAutoDismiss" Label="Auto Dismiss" /> - <BitNumberField @bind-Value="basicSnackBarDismissSeconds" Step="1" Min="1" Label="Dismiss Time (based on second)" /> + <BitToggle @bind-Value="basicSnackBarAutoDismiss" Label="Auto Dismiss" Inline /> + <BitNumberField @bind-Value="basicSnackBarDismissSeconds" IsEnabled="basicSnackBarAutoDismiss" Step="1" Min="1" Label="Dismiss Time (based on second)" /> + <br /><br /> + <BitToggle @bind-Value="basicSnackBarMultiline" Label="Multiline" Inline /> + <br /><br /> + <BitTextField @bind-Value="basicSnackBarTitle" Label="Title" DefaultValue="Title" /> + <BitTextField @bind-Value="basicSnackBarBody" Label="Body" Multiline Rows="6" DefaultValue="This is a body!" /> </div> </div> <br /> - <BitButton OnClick="OpenCustomizationSnackBar">Show</BitButton> </ExamplePreview> </ComponentExampleBox> @@ -111,7 +127,7 @@ Header = "background-color: rebeccapurple; padding: 0.2rem;" })" /> <BitButton OnClick="OpenSnackBarStyles">Custom styles</BitButton> <br /><br /> - <BitSnackBar @ref="snackBarClassesRef" + <BitSnackBar @ref="snackBarClassesRef" AutoDismiss Classes="@(new() { Container = "custom-container", ProgressBar = "custom-progress" })" /> <BitButton OnClick="OpenSnackBarClasses">Custom classes</BitButton> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.cs index 32a70bf80f..248dca5ad1 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.cs @@ -8,51 +8,58 @@ public partial class BitSnackBarDemo { Name = "AutoDismiss", Type = "bool", - DefaultValue = "true", - Description = "Whether or not to dismiss itself automatically.", + DefaultValue = "false", + Description = "Whether or not automatically dismiss the snack bar.", }, new() { Name = "AutoDismissTime", - Type = "TimeSpan", - DefaultValue = "TimeSpan.FromSeconds(3)", - Description = "How long the SnackBar will automatically dismiss (default is 3 seconds).", + Type = "TimeSpan?", + DefaultValue = "null", + Description = "How long does it take to automatically dismiss the snack bar (default is 3 seconds).", }, new() { Name = "BodyTemplate", Type = "RenderFragment<string>?", DefaultValue = "null", - Description = "Used to customize how content inside the Body is rendered.", + Description = "Used to customize how the content inside the body is rendered.", }, new() { Name = "Classes", Type = "BitSnackBarClassStyles?", DefaultValue = "null", + Description = "Custom CSS classes for different parts of the snack bar.", LinkType = LinkType.Link, Href = "#snackbar-class-styles", - Description = "Custom CSS classes for different parts of the BitSnackBar.", }, new() { Name = "DismissIconName", Type = "string?", DefaultValue = "null", - Description = "Dismiss Icon in SnackBar.", + Description = "The icon name of the dismiss button.", + }, + new() + { + Name = "Multiline", + Type = "bool", + DefaultValue = "false", + Description = "Enables the multiline mode of both title and body.", }, new() { Name = "OnDismiss", Type = "EventCallback", - Description = "Callback for when the Dismissed.", + Description = "Callback for when any snack bar is dismissed.", }, new() { Name = "Position", - Type = "BitSnackBarPosition", - DefaultValue = "BitSnackBarPosition.BottomRight", - Description = "The position of SnackBar to show.", + Type = "BitSnackBarPosition?", + DefaultValue = "null", + Description = "The position of the snack bars to show.", LinkType = LinkType.Link, Href = "#snackbar-position-enum" }, @@ -60,17 +67,17 @@ public partial class BitSnackBarDemo { Name = "Styles", Type = "BitSnackBarClassStyles?", + Description = "Custom CSS styles for different parts of the snack bar.", DefaultValue = "null", LinkType = LinkType.Link, Href = "#snackbar-class-styles", - Description = "Custom CSS styles for different parts of the BitSnackBar.", }, new() { Name = "TitleTemplate", Type = "RenderFragment<string>?", DefaultValue = "null", - Description = "Used to customize how content inside the Title is rendered. ", + Description = "Used to customize how content inside the title is rendered. ", }, ]; @@ -84,7 +91,7 @@ public partial class BitSnackBarDemo [ new() { - Name = "TopLeft", + Name = "TopStart", Value = "0", }, new() @@ -94,12 +101,12 @@ public partial class BitSnackBarDemo }, new() { - Name = "TopRight", + Name = "TopEnd", Value = "2", }, new() { - Name = "BottomLeft", + Name = "BottomStart", Value = "3", }, new() @@ -109,52 +116,119 @@ public partial class BitSnackBarDemo }, new() { - Name = "BottomRight", + Name = "BottomEnd", Value = "5", }, ] }, new() { - Id = "snackbar-type-enum", - Name = "BitSnackBarType", + Id = "color-enum", + Name = "BitColor", + Description = "Defines the general colors available in the bit BlazorUI.", Items = [ new() { - Name = "None", - Description = "None styled SnackBar", + Name = "Primary", + Description = "Info Primary general color.", Value = "0", }, new() { - Name = "Info", - Description = "Info styled SnackBar", + Name = "Secondary", + Description = "Secondary general color.", Value = "1", }, new() { - Name = "Warning", - Description = "Warning styled SnackBar", + Name = "Tertiary", + Description = "Tertiary general color.", Value = "2", }, new() { - Name = "Success", - Description = "Success styled SnackBar", + Name = "Info", + Description = "Info general color.", Value = "3", }, new() { - Name = "Error", - Description = "Error styled SnackBar", + Name = "Success", + Description = "Success general color.", Value = "4", }, new() { - Name = "SevereWarning", - Description = "Severe Warning styled SnackBar", + Name = "Warning", + Description = "Warning general color.", Value = "5", + }, + new() + { + Name = "SevereWarning", + Description = "SevereWarning general color.", + Value = "6", + }, + new() + { + Name = "Error", + Description = "Error general color.", + Value = "7", + }, + new() + { + Name = "PrimaryBackground", + Description = "Primary background color.", + Value = "8", + }, + new() + { + Name = "SecondaryBackground", + Description = "Secondary background color.", + Value = "9", + }, + new() + { + Name = "TertiaryBackground", + Description = "Tertiary background color.", + Value = "10", + }, + new() + { + Name = "PrimaryForeground", + Description = "Primary foreground color.", + Value = "11", + }, + new() + { + Name = "SecondaryForeground", + Description = "Secondary foreground color.", + Value = "12", + }, + new() + { + Name = "TertiaryForeground", + Description = "Tertiary foreground color.", + Value = "13", + }, + new() + { + Name = "PrimaryBorder", + Description = "Primary border color.", + Value = "14", + }, + new() + { + Name = "SecondaryBorder", + Description = "Secondary border color.", + Value = "15", + }, + new() + { + Name = "TertiaryBorder", + Description = "Tertiary border color.", + Value = "16", } ] } @@ -233,7 +307,7 @@ public partial class BitSnackBarDemo new() { Name = "Show", - Type = "async Task Show(string title, string? body = \"\", BitSnackBarType type = BitSnackBarType.None, string? cssClass = null, string? cssStyle = null)", + Type = "async Task Show(string title, string? body = \"\", BitColor color = BitColor.Info, string? cssClass = null, string? cssStyle = null)", DefaultValue = "", Description = "Shows the snackbar.", }, @@ -242,28 +316,35 @@ public partial class BitSnackBarDemo Name = "Info", Type = "Task Info(string title, string? body = \"\")", DefaultValue = "", - Description = "Shows the snackbar with Info type.", + Description = "Shows the snackbar with Info color.", }, new() { Name = "Success", Type = "Task Success(string title, string? body = \"\")", DefaultValue = "", - Description = "Shows the snackbar with Success type.", + Description = "Shows the snackbar with Success color.", }, new() { Name = "Warning", Type = "Task Warning(string title, string? body = \"\")", DefaultValue = "", - Description = "Shows the snackbar with Warning type.", + Description = "Shows the snackbar with Warning color.", + }, + new() + { + Name = "SevereWarning", + Type = "Task Warning(string title, string? body = \"\")", + DefaultValue = "", + Description = "Shows the snackbar with SevereWarning color.", }, new() { Name = "Error", Type = "Task Error(string title, string? body = \"\")", DefaultValue = "", - Description = "Shows the snackbar with Error type.", + Description = "Shows the snackbar with Error color.", } ]; @@ -298,17 +379,18 @@ private async Task OpenBodyTemplate() private BitDir direction; + private bool basicSnackBarMultiline; + private bool basicSnackBarAutoDismiss; private int basicSnackBarDismissSeconds = 3; - private bool basicSnackBarAutoDismiss = true; private BitSnackBar customizationRef = default!; private string basicSnackBarBody = "This is body"; private string basicSnackBarTitle = "This is title"; - private BitSnackBarType basicSnackBarType = BitSnackBarType.Info; - private BitSnackBarPosition basicSnackBarPosition = BitSnackBarPosition.BottomRight; + private BitColor basicSnackBarColor = BitColor.Info; + private BitSnackBarPosition basicSnackBarPosition = BitSnackBarPosition.BottomEnd; private async Task OpenCustomizationSnackBar() { - await customizationRef.Show(basicSnackBarTitle, basicSnackBarBody, basicSnackBarType); + await customizationRef.Show(basicSnackBarTitle, basicSnackBarBody, basicSnackBarColor); } @@ -356,21 +438,35 @@ private async Task OpenBasicSnackBar() AutoDismiss=""@basicSnackBarAutoDismiss"" AutoDismissTime=""TimeSpan.FromSeconds(basicSnackBarDismissSeconds)"" /> -<BitChoiceGroup @bind-Value=""basicSnackBarType"" Label=""Type"" TItem=""BitChoiceGroupOption<BitSnackBarType>"" TValue=""BitSnackBarType""> - <BitChoiceGroupOption Text=""Info"" Value=""BitSnackBarType.Info"" /> - <BitChoiceGroupOption Text=""Success"" Value=""BitSnackBarType.Success"" /> - <BitChoiceGroupOption Text=""Warning"" Value=""BitSnackBarType.Warning"" /> - <BitChoiceGroupOption Text=""SevereWarning"" Value=""BitSnackBarType.SevereWarning"" /> - <BitChoiceGroupOption Text=""Error"" Value=""BitSnackBarType.Error"" /> +<BitButton OnClick=""OpenBasicSnackBar"">Show</BitButton> + +<BitChoiceGroup @bind-Value=""basicSnackBarColor"" Label=""Type"" TItem=""BitChoiceGroupOption<BitColor>"" TValue=""BitColor""> + <BitChoiceGroupOption Text=""Primary"" Value=""BitColor.Primary"" /> + <BitChoiceGroupOption Text=""Secondary"" Value=""BitColor.Secondary"" /> + <BitChoiceGroupOption Text=""Tertiary"" Value=""BitColor.Tertiary"" /> + <BitChoiceGroupOption Text=""Info"" Value=""BitColor.Info"" /> + <BitChoiceGroupOption Text=""Success"" Value=""BitColor.Success"" /> + <BitChoiceGroupOption Text=""Warning"" Value=""BitColor.Warning"" /> + <BitChoiceGroupOption Text=""SevereWarning"" Value=""BitColor.SevereWarning"" /> + <BitChoiceGroupOption Text=""Error"" Value=""BitColor.Error"" /> + <BitChoiceGroupOption Text=""PrimaryBackground"" Value=""BitColor.PrimaryBackground"" /> + <BitChoiceGroupOption Text=""SecondaryBackground"" Value=""BitColor.SecondaryBackground"" /> + <BitChoiceGroupOption Text=""TertiaryBackground"" Value=""BitColor.TertiaryBackground"" /> + <BitChoiceGroupOption Text=""PrimaryForeground"" Value=""BitColor.PrimaryForeground"" /> + <BitChoiceGroupOption Text=""SecondaryForeground"" Value=""BitColor.SecondaryForeground"" /> + <BitChoiceGroupOption Text=""TertiaryForeground"" Value=""BitColor.TertiaryForeground"" /> + <BitChoiceGroupOption Text=""PrimaryBorder"" Value=""BitColor.PrimaryBorder"" /> + <BitChoiceGroupOption Text=""SecondaryBorder"" Value=""BitColor.SecondaryBorder"" /> + <BitChoiceGroupOption Text=""TertiaryBorder"" Value=""BitColor.TertiaryBorder"" /> </BitChoiceGroup> <BitChoiceGroup @bind-Value=""basicSnackBarPosition"" Label=""Position"" TItem=""BitChoiceGroupOption<BitSnackBarPosition>"" TValue=""BitSnackBarPosition""> - <BitChoiceGroupOption Text=""TopLeft"" Value=""BitSnackBarPosition.TopLeft"" /> + <BitChoiceGroupOption Text=""TopStart"" Value=""BitSnackBarPosition.TopStart"" /> <BitChoiceGroupOption Text=""TopCenter"" Value=""BitSnackBarPosition.TopCenter"" /> - <BitChoiceGroupOption Text=""TopRight"" Value=""BitSnackBarPosition.TopRight"" /> - <BitChoiceGroupOption Text=""BottomLeft"" Value=""BitSnackBarPosition.BottomLeft"" /> + <BitChoiceGroupOption Text=""TopEnd"" Value=""BitSnackBarPosition.TopEnd"" /> + <BitChoiceGroupOption Text=""BottomStart"" Value=""BitSnackBarPosition.BottomStart"" /> <BitChoiceGroupOption Text=""BottomCenter"" Value=""BitSnackBarPosition.BottomCenter"" /> - <BitChoiceGroupOption Text=""BottomRight"" Value=""BitSnackBarPosition.BottomRight"" /> + <BitChoiceGroupOption Text=""BottomEnd"" Value=""BitSnackBarPosition.BottomEnd"" /> </BitChoiceGroup> <BitChoiceGroup @bind-Value=""direction"" Label=""Direction"" TItem=""BitChoiceGroupOption<BitDir>"" TValue=""BitDir""> @@ -379,26 +475,27 @@ private async Task OpenBasicSnackBar() <BitChoiceGroupOption Text=""Auto"" Value=""BitDir.Auto"" /> </BitChoiceGroup> -<BitTextField @bind-Value=""basicSnackBarTitle"" Label=""Title"" DefaultValue=""Title"" /> -<BitTextField @bind-Value=""basicSnackBarBody"" Label=""Body"" IsMultiline=""true"" Rows=""6"" DefaultValue=""This is a body!"" /> - <BitToggle @bind-Value=""basicSnackBarAutoDismiss"" Label=""Auto Dismiss"" /> <BitNumberField @bind-Value=""basicSnackBarDismissSeconds"" Step=""1"" Min=""1"" Label=""Dismiss Time (based on second)"" /> -<BitButton OnClick=""OpenBasicSnackBar"">Show</BitButton>"; +<BitToggle @bind-Value=""basicSnackBarMultiline"" Label=""Multiline"" Inline /> + +<BitTextField @bind-Value=""basicSnackBarTitle"" Label=""Title"" DefaultValue=""Title"" /> +<BitTextField @bind-Value=""basicSnackBarBody"" Label=""Body"" Multiline Rows=""6"" DefaultValue=""This is a body!"" />"; private readonly string example2CsharpCode = @" private BitDir direction; +private bool basicSnackBarMultiline; +private bool basicSnackBarAutoDismiss; private int basicSnackBarDismissSeconds = 3; -private bool basicSnackBarAutoDismiss = true; private BitSnackBar customizationRef = default!; private string basicSnackBarBody = ""This is body""; private string basicSnackBarTitle = ""This is title""; -private BitSnackBarType basicSnackBarType = BitSnackBarType.Info; -private BitSnackBarPosition basicSnackBarPosition = BitSnackBarPosition.BottomRight; +private BitColor basicSnackBarColor = BitColor.Info; +private BitSnackBarPosition basicSnackBarPosition = BitSnackBarPosition.BottomEnd; private async Task OpenBasicSnackBar() { - await basicSnackBarRef.Show(basicSnackBarTitle, basicSnackBarBody, basicSnackBarType); + await basicSnackBarRef.Show(basicSnackBarTitle, basicSnackBarBody, basicSnackBarColor); }"; private readonly string example3RazorCode = @" @@ -461,7 +558,7 @@ private async Task OpenBodyTemplate() } .custom-progress { - background-color: gold; + background-color: red; } </style> @@ -477,7 +574,7 @@ private async Task OpenBodyTemplate() Header = ""background-color: rebeccapurple; padding: 0.2rem;"" })"" /> <BitButton OnClick=""OpenSnackBarStyles"">Custom styles</BitButton> -<BitSnackBar @ref=""snackBarClassesRef"" +<BitSnackBar @ref=""snackBarClassesRef"" AutoDismiss Classes=""@(new() { Container = ""custom-container"", ProgressBar = ""custom-progress"" })"" /> <BitButton OnClick=""OpenSnackBarClasses"">Custom classes</BitButton>"; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.scss index 61661dd22a..08d16608f2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/SnackBar/BitSnackBarDemo.razor.scss @@ -19,6 +19,6 @@ } .custom-progress { - background-color: gold; + background-color: red; } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor index 530a1d02ed..0937173c3a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor @@ -27,12 +27,16 @@ <ComponentExampleBox Title="Icon" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> + <div>Display an icon within a BitTag to represent its content visually.</div> + <br /> <BitTag Text="Calendar icon" IconName="@BitIconName.Calendar" /> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Dismiss" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> + <div>Use the OnDismiss feature in BitTag to provide a close button for the tag content.</div> + <br /> @if (isDismissed is false) { <BitTag IconName="@BitIconName.AlarmClock" Text="Dismiss me" OnDismiss="() => isDismissed = true" /> @@ -103,6 +107,8 @@ <ComponentExampleBox Title="Template" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> + <div>Customize the content inside BitTag using a template with different elements and components.</div> + <br /> <BitTag> <BitStack Horizontal Gap="0.5rem" Style="padding-inline: 0.5rem;"> <BitLabel>Custom content</BitLabel> @@ -115,7 +121,7 @@ <ComponentExampleBox Title="Style & Class" RazorCode="@example8RazorCode" Id="example8"> <ExamplePreview> <div>Empower customization by overriding default styles and classes, allowing tailored design modifications to suit specific UI requirements.</div> - <br /> + <br /><br /> <div>Component's Style & Class:</div> <br /> <div> @@ -130,8 +136,9 @@ <br /> <BitTag Text="Styles" IconName="@BitIconName.People" - Styles="@(new() { Root = "border-color: darkblue; border-width: 0.25rem;", - Text = "color: darkblue; font-weight: 600;" })" /> + Styles="@(new() { Root = "border-color: red; background-color: transparent;", + Text = "color: tomato; font-weight: bold;", + Icon = "color: tomato;" })" /> <BitTag Text="Classes" IconName="@BitIconName.People" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs index 1ea4f297fb..b94345254c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs @@ -340,8 +340,9 @@ public partial class BitTagDemo .custom-root { color: mediumpurple; - border-color: mediumpurple; border-radius: 0.5rem; + border-color: mediumpurple; + background-color: transparent; box-shadow: mediumpurple 0 0 0.5rem; } @@ -363,8 +364,9 @@ public partial class BitTagDemo <BitTag Text=""Styles"" IconName=""@BitIconName.People"" - Styles=""@(new() { Root = ""border-color: darkblue; border-width: 0.25rem;"", - Text = ""color: darkblue; font-weight: 600;"" })"" /> + Styles=""@(new() { Root = ""border-color: red; background-color: transparent;"", + Text = ""color: tomato; font-weight: bold;"", + Icon = ""color: tomato;"" })"" /> <BitTag Text=""Classes"" IconName=""@BitIconName.People"" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.scss index 02f448dd03..e4c168ee4c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.scss @@ -6,8 +6,9 @@ .custom-root { color: mediumpurple; - border-color: mediumpurple; border-radius: 0.5rem; + border-color: mediumpurple; + background-color: transparent; box-shadow: mediumpurple 0 0 0.5rem; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor index 2ba12f927a..0949330146 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor @@ -10,146 +10,128 @@ ComponentSubClasses="componentSubClasses"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitAccordion Title="Accordion 1"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. - </BitAccordion> - </div> - - <div class="example-desc">You can define multiple accordions together.</div> - <div class="example-box"> - <BitAccordion Title="Accordion 1"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. - </BitAccordion> - <BitAccordion Title="Accordion 2"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. - </BitAccordion> - <BitAccordion Title="Accordion 3"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. - </BitAccordion> - </div> + <BitAccordion Title="Accordion"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. + </BitAccordion> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Description" RazorCode="@example2RazorCode" Id="example2"> + <ComponentExampleBox Title="NoBorder" RazorCode="@example2RazorCode" Id="example2"> <ExamplePreview> - <div class="example-desc">You can set a short description inside the Accordion header.</div> - <div class="example-box"> - <BitAccordion Title="General settings" Description="I am an accordion"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. - </BitAccordion> - <BitAccordion Title="Users" Description="You are currently not an owner"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. - </BitAccordion> - <BitAccordion Title="Advanced settings" Description="Filtering has been entirely disabled for whole web server"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. - </BitAccordion> - <BitAccordion Title="Advanced settings" Description="Filtering has been entirely disabled for whole web server"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. - </BitAccordion> - </div> + <BitAccordion Title="Accordion" NoBorder> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. + </BitAccordion> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Controlled" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> + <ComponentExampleBox Title="Multiple" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> - <div class="example-desc">Only one Accordion can be open.</div> - <div class="example-box"> - <BitAccordion Title="General settings" - Description="I am an accordion" - OnClick="() => controlledAccordionExpandedItem = 1" - IsExpanded="controlledAccordionExpandedItem == 1"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. - </BitAccordion> - <BitAccordion Title="Users" - Description="You are currently not an owner" - OnClick="() => controlledAccordionExpandedItem = 2" - IsExpanded="controlledAccordionExpandedItem == 2"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. - </BitAccordion> - <BitAccordion Title="Advanced settings" - Description="Filtering has been entirely disabled for whole web server" - OnClick="() => controlledAccordionExpandedItem = 3" - IsExpanded="controlledAccordionExpandedItem == 3"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. - </BitAccordion> - </div> + <div>You can define multiple accordions together.</div><br /> + <BitAccordion Title="Accordion 1"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. + </BitAccordion> + <BitAccordion Title="Accordion 2"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. + </BitAccordion> + <BitAccordion Title="Accordion 3"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. + </BitAccordion> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Binding" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ComponentExampleBox Title="Description" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> - <div class="example-desc">You can bind values with Accordion.</div> - <div class="example-operator-box"> - <BitToggle @bind-Value="AccordionToggleIsEnabled" OnText="Enabled" OffText="Disabled" Style="margin-right: 10px;" /> - <BitToggle @bind-Value="AccordionToggleIsExpanded" OnText="Expanded" OffText="Collapsed" /> - </div> - <div class="example-box"> - <BitAccordion Title="Accordion 1" - Description="I am an accordion" - IsEnabled="@AccordionToggleIsEnabled" - @bind-IsExpanded="AccordionToggleIsExpanded"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. - </BitAccordion> - </div> + <div>You can set a short description inside the Accordion header.</div><br /> + <BitAccordion Title="General settings" Description="The general settings of the application"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. + </BitAccordion> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Customization" RazorCode="@example5RazorCode" Id="example5"> + <ComponentExampleBox Title="Controlled" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-desc">This Accordion header is customized.</div> - <div class="example-box"> - <BitAccordion> - <HeaderTemplate Context="isExpanded"> - <BitButton Variant="BitVariant.Text" IconName="@(isExpanded ? BitIconName.ChevronDown : BitIconName.ChevronRight)" /> - <div class="custom-header"> - <span class="custom-title">Accordion 1</span> - <span>I am an accordion</span> - </div> - </HeaderTemplate> - <ChildContent> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. - </ChildContent> - </BitAccordion> - </div> + <div>You can control the accordions to open only one at a time.</div><br /> + <BitAccordion Title="General settings" + Description="I am an accordion" + OnClick="() => controlledAccordionExpandedItem = 1" + IsExpanded="controlledAccordionExpandedItem == 1"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. + </BitAccordion> + <BitAccordion Title="Users" + Description="You are currently not an owner" + OnClick="() => controlledAccordionExpandedItem = 2" + IsExpanded="controlledAccordionExpandedItem == 2"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. + </BitAccordion> + <BitAccordion Title="Advanced settings" + Description="Filtering has been entirely disabled for whole web server" + OnClick="() => controlledAccordionExpandedItem = 3" + IsExpanded="controlledAccordionExpandedItem == 3"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. + </BitAccordion> + </ExamplePreview> + </ComponentExampleBox> - <div class="example-desc">This Accordion content is customized by the BitCarousel.</div> - <div class="example-box"> - <BitAccordion Title="Nature" Description="I am an accordion"> - <BitCarousel AnimationDuration="1"> - <BitCarouselItem> - <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img1.jpg"> - </BitCarouselItem> - <BitCarouselItem> - <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img2.jpg" /> - </BitCarouselItem> - <BitCarouselItem> - <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img3.jpg" /> - </BitCarouselItem> - <BitCarouselItem> - <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img4.jpg" /> - </BitCarouselItem> - </BitCarousel> - </BitAccordion> - </div> + <ComponentExampleBox Title="Binding" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ExamplePreview> + <div>You can bind values with Accordion.</div><br /> + <BitToggle @bind-Value="AccordionToggleIsEnabled" OnText="Enabled" OffText="Disabled" /> + <br /> + <BitToggle @bind-Value="AccordionToggleIsExpanded" OnText="Expanded" OffText="Collapsed" /> + <br /><br /> + <BitAccordion Title="Accordion" + Description="I am an accordion" + IsEnabled="AccordionToggleIsEnabled" + @bind-IsExpanded="AccordionToggleIsExpanded"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. + </BitAccordion> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example6RazorCode" Id="example6"> + <ComponentExampleBox Title="Customization" RazorCode="@example7RazorCode" Id="example7"> <ExamplePreview> - <div class="example-box"> - <BitAccordion Dir="BitDir.Rtl" - Title="تنظیمات" - Description="من یک آکاردئون هستم!"> - لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. - </BitAccordion> - </div> + <div>This Accordion header is customized.</div><br /> + <BitAccordion> + <HeaderTemplate Context="isExpanded"> + <BitButton Variant="BitVariant.Text" IconName="@(isExpanded ? BitIconName.ChevronDown : BitIconName.ChevronRight)" /> + <div class="custom-header"> + <span class="custom-title">Accordion 1</span> + <span class="custom-desc">I am an accordion</span> + </div> + </HeaderTemplate> + <ChildContent> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. + </ChildContent> + </BitAccordion> + <br /><br /> + <div>This Accordion content is customized by the BitCarousel.</div><br /> + <BitAccordion Title="Nature" Description="I am an accordion"> + <BitCarousel AnimationDuration="1"> + <BitCarouselItem> + <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img1.jpg"> + </BitCarouselItem> + <BitCarouselItem> + <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img2.jpg" /> + </BitCarouselItem> + <BitCarouselItem> + <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img3.jpg" /> + </BitCarouselItem> + <BitCarouselItem> + <img src="/_content/Bit.BlazorUI.Demo.Client.Core/images/carousel/img4.jpg" /> + </BitCarouselItem> + </BitCarousel> + </BitAccordion> </ExamplePreview> </ComponentExampleBox> -</ComponentDemo> -@code { - private byte controlledAccordionExpandedItem = 1; - private bool AccordionToggleIsEnabled; - private bool AccordionToggleIsExpanded; -} \ No newline at end of file + <ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" Id="example8"> + <ExamplePreview> + <div>Use BitAccordion in right-to-left (RTL).</div><br /> + <BitAccordion Dir="BitDir.Rtl" + Title="تنظیمات" + Description="من یک آکاردئون هستم!"> + لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. + </BitAccordion> + </ExamplePreview> + </ComponentExampleBox> +</ComponentDemo> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.cs index 28b7881514..5f65b49033 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.cs @@ -4,12 +4,19 @@ public partial class BitAccordionDemo { private readonly List<ComponentParameter> componentParameters = [ + new() + { + Name = "Body", + Type = "RenderFragment?", + DefaultValue = "null", + Description = "Alias for the ChildContent parameter." + }, new() { Name = "Classes", Type = "BitAccordionClassStyles?", DefaultValue = "null", - Description = "Custom CSS classes for different parts of the BitAccordion.", + Description = "Custom CSS classes for different parts of the accordion.", LinkType = LinkType.Link, Href = "#accordion-class-styles" }, @@ -18,35 +25,42 @@ public partial class BitAccordionDemo Name = "ChildContent", Type = "RenderFragment?", DefaultValue = "null", - Description = "The content of the Accordion." + Description = "The content of the accordion." }, new() { Name = "DefaultIsExpanded", Type = "bool?", DefaultValue = "null", - Description = "Default value of the IsExpanded." + Description = "Default value for the IsExpanded parameter." }, new() { Name = "Description", Type = "string?", DefaultValue = "null", - Description = "A short description in the header of Accordion." + Description = "A short description in the header of the accordion." }, new() { Name = "HeaderTemplate", Type = "RenderFragment<bool>?", DefaultValue = "null", - Description = "Used to customize how the header inside the Accordion is rendered." + Description = "Used to customize the header of the accordion." }, new() { Name = "IsExpanded", Type = "bool", DefaultValue = "false", - Description = "Determines whether the accordion is expanding or collapses." + Description = "Determines whether the accordion is expanded or collapsed." + }, + new() + { + Name = "NoBorder", + Type = "bool", + DefaultValue = "false", + Description = "Removes the default border of the accordion and gives a background color to the body." }, new() { @@ -65,7 +79,7 @@ public partial class BitAccordionDemo Name = "Styles", Type = "BitAccordionClassStyles?", DefaultValue = "null", - Description = "Custom CSS styles for different parts of the BitAccordion.", + Description = "Custom CSS styles for different parts of the accordion.", LinkType = LinkType.Link, Href = "#accordion-class-styles" }, @@ -136,6 +150,13 @@ public partial class BitAccordionDemo Description = "Custom CSS classes/styles for the chevron down icon of the BitAccordion." }, new() + { + Name = "ContentContainer", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the content container of the BitAccordion." + }, + new() { Name = "Content", Type = "string?", @@ -148,11 +169,24 @@ public partial class BitAccordionDemo + private byte controlledAccordionExpandedItem = 1; + + private bool AccordionToggleIsEnabled; + private bool AccordionToggleIsExpanded; + + + private readonly string example1RazorCode = @" -<BitAccordion Title=""Accordion 1""> +<BitAccordion Title=""Accordion""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. -</BitAccordion> +</BitAccordion>"; + + private readonly string example2RazorCode = @" +<BitAccordion Title=""Accordion"" NoBorder> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. +</BitAccordion>"; + private readonly string example3RazorCode = @" <BitAccordion Title=""Accordion 1""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. </BitAccordion> @@ -163,75 +197,63 @@ public partial class BitAccordionDemo Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. Duis lobortis tristique nunc, id egestas ligula condimentum quis. Integer elementum tempor cursus. Phasellus vestibulum neque non laoreet faucibus. Nunc eu congue urna, in dapibus justo. </BitAccordion>"; - private readonly string example2RazorCode = @" -<BitAccordion Title=""General settings"" Description=""I am an accordion""> + private readonly string example4RazorCode = @" +<BitAccordion Title=""General settings"" Description=""The general settings of the application""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. -</BitAccordion> - -<BitAccordion Title=""Users"" Description=""You are currently not an owner""> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. -</BitAccordion> - -<BitAccordion Title=""Advanced settings"" Description=""Filtering has been entirely disabled for whole web server""> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. -</BitAccordion> - -<BitAccordion Title=""Advanced settings"" Description=""Filtering has been entirely disabled for whole web server""> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. </BitAccordion>"; - private readonly string example3RazorCode = @" + private readonly string example5RazorCode = @" <BitAccordion Title=""General settings"" Description=""I am an accordion"" OnClick=""() => controlledAccordionExpandedItem = 1"" IsExpanded=""controlledAccordionExpandedItem == 1""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. Sed consequat condimentum massa, non euismod magna gravida vitae. Donec rhoncus suscipit blandit. Nunc ultrices vulputate nisl. </BitAccordion> - <BitAccordion Title=""Users"" Description=""You are currently not an owner"" OnClick=""() => controlledAccordionExpandedItem = 2"" IsExpanded=""controlledAccordionExpandedItem == 2""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. </BitAccordion> - <BitAccordion Title=""Advanced settings"" Description=""Filtering has been entirely disabled for whole web server"" OnClick=""() => controlledAccordionExpandedItem = 3"" IsExpanded=""controlledAccordionExpandedItem == 3""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. </BitAccordion>"; - private readonly string example3CsharpCode = @" + private readonly string example5CsharpCode = @" private byte controlledAccordionExpandedItem = 1;"; - private readonly string example4RazorCode = @" -<BitToggle @bind-Value=""AccordionToggleIsEnabled"" OnText=""Enabled"" OffText=""Disabled"" Style=""margin-right: 10px;"" /> -<BitToggle @bind-Value=""AccordionToggleIsEnabled"" OnText=""Expanded"" OffText=""Collapsed"" /> + private readonly string example6RazorCode = @" +<BitToggle @bind-Value=""AccordionToggleIsEnabled"" OnText=""Enabled"" OffText=""Disabled"" /> +<BitToggle @bind-Value=""AccordionToggleIsExpanded"" OnText=""Expanded"" OffText=""Collapsed"" /> -<BitAccordion Title=""Accordion 1"" - Description=""I am an accordion"" - IsEnabled=""@AccordionToggleIsEnabled"" - @bind-IsExpanded=""AccordionToggleIsExpanded""> +<BitAccordion Title=""Accordion"" + Description=""I am an accordion"" + IsEnabled=""AccordionToggleIsEnabled"" + @bind-IsExpanded=""AccordionToggleIsExpanded""> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis a elit vel lacus tincidunt dignissim. Phasellus mollis mauris orci, eget fermentum diam porta eu. Integer a consequat sapien, pellentesque aliquam velit. Nullam quis ligula vitae nisi accumsan auctor. Ut faucibus nulla a est commodo, vel sagittis neque tristique. In nec urna hendrerit, iaculis turpis sed, dictum elit. Sed id sagittis nunc, vitae ornare elit. </BitAccordion>"; - - private readonly string example4CsharpCode = @" + private readonly string example6CsharpCode = @" private bool AccordionToggleIsEnabled; private bool AccordionToggleIsExpanded;"; - private readonly string example5RazorCode = @" + private readonly string example7RazorCode = @" <style> .custom-header { + gap: 1rem; flex-grow: 1; display: flex; - color: #0054C6; line-height: 1.5; + align-items: center; } .custom-title { - width: 30%; - font-weight: 600; - font-size: rem(16px); + color: #0054C6; + } + + .custom-desc { + color: brown; } </style> @@ -240,7 +262,7 @@ public partial class BitAccordionDemo <BitButton Variant=""BitVariant.Text"" IconName=""@(isExpanded ? BitIconName.ChevronDown : BitIconName.ChevronRight)"" /> <div class=""custom-header""> <span class=""custom-title"">Accordion 1</span> - <span>I am an accordion</span> + <span class=""custom-desc"">I am an accordion</span> </div> </HeaderTemplate> <ChildContent> @@ -265,11 +287,10 @@ public partial class BitAccordionDemo </BitCarousel> </BitAccordion>"; - private readonly string example6RazorCode = @" + private readonly string example8RazorCode = @" <BitAccordion Dir=""BitDir.Rtl"" Title=""تنظیمات"" Description=""من یک آکاردئون هستم!""> لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. -</BitAccordion> -"; +</BitAccordion>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.scss index d0446b3dc3..3a20047aba 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Accordion/BitAccordionDemo.razor.scss @@ -1,42 +1,17 @@ @import '../../../../Styles/abstracts/_functions.scss'; -.example-desc { - margin-top: rem2(20px); -} - -.example-box { +.custom-header { + gap: 1rem; + flex-grow: 1; display: flex; - padding: rem2(30px) 0; - border-radius: rem2(2px); - flex-flow: column nowrap; + line-height: 1.5; + align-items: center; } -.example-operator-box { - margin: rem2(15px) 0; +.custom-title { + color: #0054C6; } -::deep { - .custom-header { - flex-grow: 1; - display: flex; - color: #0054C6; - line-height: 1.5; - align-items: center; - } - - .custom-title { - width: 30%; - font-weight: 600; - font-size: rem2(16px); - } - - @media only screen and (max-width: #{rem2(639px)}) { - .custom-header { - flex-direction: column; - } - - .custom-title { - width: 100%; - } - } +.custom-desc { + color: brown; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor index 115fe3f308..a6dee547f5 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor @@ -11,77 +11,81 @@ ComponentSubEnums="componentSubEnums"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <div> - <BitButton OnClick="@(() => IsOpen = true)">Open Dialog</BitButton> - <BitDialog @bind-IsOpen="IsOpen" Title="Missing Subject" Message="Do you want to send this message without a subject?" /> - </div> + <BitButton OnClick="@(() => IsOpen = true)">Open Dialog</BitButton> + <BitDialog @bind-IsOpen="IsOpen" Title="Missing Subject" Message="Do you want to send this message without a subject?" /> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Result" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div> - <BitButton OnClick="@(() => IsOpen1 = true)">Open Dialog</BitButton> - <br /><br /> - <span>Result is: @dialogRef?.Result</span> - <BitDialog @ref="@dialogRef" - @bind-IsOpen="@IsOpen1" - Title="Missing Subject" - Message="Do you want to send this message without a subject?" /> - </div> + <BitButton OnClick="@(() => IsOpen1 = true)">Open Dialog</BitButton> + <br /><br /> + <span>Result is: @dialogRef?.Result</span> + <BitDialog @ref="@dialogRef" + @bind-IsOpen="@IsOpen1" + Title="Missing Subject" + Message="Do you want to send this message without a subject?" /> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Custom content" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> + <ComponentExampleBox Title="Events" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div> - <BitButton OnClick="@(() => IsOpen2 = true)">Open Dialog</BitButton> - <br /><br /> - <div>Result is: @customDialogRef?.Result</div> - @if (customDialogRef?.Result == BitDialogResult.Ok) - { - <div>Value is: @optionValue</div> - } - - <BitDialog @ref="customDialogRef" @bind-IsOpen="@IsOpen2" ShowCloseButton="false"> + <BitButton OnClick="@(() => IsOpenEvent = true)">Open Dialog</BitButton> + <BitDialog @bind-IsOpen="IsOpenEvent" + Title="Missing Subject" + Message="Do you want to send this message without a subject?" + OnOk="async () => await Task.Delay(1000)" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Custom content" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ExamplePreview> + <BitButton OnClick="@(() => IsOpen2 = true)">Open Dialog</BitButton> + <br /><br /> + <div>Result is: @customDialogRef?.Result</div> + @if (customDialogRef?.Result == BitDialogResult.Ok) + { + <div>Value is: @optionValue</div> + } + + <BitDialog @ref="customDialogRef" @bind-IsOpen="@IsOpen2" ShowCloseButton="false"> + <div class="dialog-title"> + <span>All emails together</span> + </div> + <div class="dialog-body"> + <p> + Your Inbox has changed. No longer does it include favorites, it is a singular destination for your emails. + </p> + <br /> + <BitChoiceGroup @bind-Value="optionValue" Label="Basic Options" TItem="BitChoiceGroupOption<string>" TValue="string"> + <BitChoiceGroupOption Text="Option A" Value="@("A")" /> + <BitChoiceGroupOption Text="Option B" Value="@("B")" /> + <BitChoiceGroupOption Text="Option C" Value="@("C")" /> + </BitChoiceGroup> + </div> + </BitDialog> + + <br /><br /> + <BitButton OnClick="@(() => IsOpen3 = true)">Open Dialog</BitButton> + <BitDialog @bind-IsOpen="@IsOpen3" ShowCloseButton="false"> + <Body> <div class="dialog-title"> - <span>All emails together</span> + Delete all </div> <div class="dialog-body"> - <p> - Your Inbox has changed. No longer does it include favorites, it is a singular destination for your emails. - </p> - <br /> - <BitChoiceGroup @bind-Value="optionValue" Label="Basic Options" TItem="BitChoiceGroupOption<string?>" TValue="string?"> - <BitChoiceGroupOption Text="Option A" Value="@("A")" /> - <BitChoiceGroupOption Text="Option B" Value="@("B")" /> - <BitChoiceGroupOption Text="Option C" Value="@("C")" /> - </BitChoiceGroup> + +99 Emails will be deleted. </div> - </BitDialog> - - <br /><br /> - <BitButton OnClick="@(() => IsOpen3 = true)">Open Dialog</BitButton> - <BitDialog @bind-IsOpen="@IsOpen3" ShowCloseButton="false"> - <Body> - <div class="dialog-title"> - Delete all - </div> - <div class="dialog-body"> - +99 Emails will be deleted. - </div> - </Body> - <FooterTemplate> - <div class="dialog-footer"> - Are you sure?! there's no going back. - </div> - </FooterTemplate> - </BitDialog> - </div> + </Body> + <FooterTemplate> + <div class="dialog-footer"> + Are you sure?! there's no going back. + </div> + </FooterTemplate> + </BitDialog> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Advanced options" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ComponentExampleBox Title="Advanced options" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> <div class="example-desc">BitDialog has some advanced options to be customized .</div> <div class="btn-container"> @@ -102,7 +106,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Absolute positioning" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> + <ComponentExampleBox Title="Absolute positioning" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> <div class="example-desc">BitDialog has an abosolute position option to further customize it's location.</div> @@ -163,7 +167,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Position" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="Position" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> <div class="example-desc">To set the Dialog position on the page you can use the Position parameter.</div> <div> @@ -185,44 +189,42 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Draggable" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> + <ComponentExampleBox Title="Draggable" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> <ExamplePreview> - <div> - <BitToggle Label="Is Draggable" @bind-Value="IsDraggable" /> - <br /> - <BitButton OnClick="@(() => IsOpen8 = true)">Open Dialog</BitButton> - <BitDialog @bind-IsOpen="IsOpen8" - IsDraggable="IsDraggable" - Title="Draggable dialog" - Message="Do you want to send this message without a subject?" /> - - <BitSeparator Style="margin: 1rem 0" /> - - <div class="example-desc">Using custom drag element.</div> - <BitButton OnClick="@(() => IsOpen9 = true)">Open Dialog</BitButton> - <BitDialog IsDraggable @bind-IsOpen="IsOpen9" ShowCloseButton="false" DragElementSelector=".dialog-title-drag"> - <div class="dialog-title dialog-title-drag"> - <span>Draggble Dialog with custom drag element</span> - <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen9 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="dialog-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - </div> - </BitDialog> - </div> + <BitToggle Label="Is Draggable" @bind-Value="IsDraggable" /> + <br /> + <BitButton OnClick="@(() => IsOpen8 = true)">Open Dialog</BitButton> + <BitDialog @bind-IsOpen="IsOpen8" + IsDraggable="IsDraggable" + Title="Draggable dialog" + Message="Do you want to send this message without a subject?" /> + + <BitSeparator Style="margin: 1rem 0" /> + + <div class="example-desc">Using custom drag element.</div> + <BitButton OnClick="@(() => IsOpen9 = true)">Open Dialog</BitButton> + <BitDialog IsDraggable @bind-IsOpen="IsOpen9" ShowCloseButton="false" DragElementSelector=".dialog-title-drag"> + <div class="dialog-title dialog-title-drag"> + <span>Draggble Dialog with custom drag element</span> + <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen9 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="dialog-body"> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitDialog> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> + <ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> <ExamplePreview> <div dir="rtl"> <BitButton Dir="BitDir.Rtl" OnClick="@(() => IsOpen10 = true)">باز کردن پنجره پیام</BitButton> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor.cs index 7d3e852b2b..915ca139bd 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Dialog/BitDialogDemo.razor.cs @@ -8,7 +8,7 @@ public partial class BitDialogDemo { Name = "AutoToggleScroll", Type = "bool", - DefaultValue = "true", + DefaultValue = "false", Description = "Enables the auto scrollbar toggle behavior of the Dialog." }, new() @@ -23,7 +23,7 @@ public partial class BitDialogDemo Name = "Body", Type = "RenderFragment?", DefaultValue = "null", - Description = "Alias for childcontent." + Description = "Alias for child content." }, new() { @@ -280,6 +280,13 @@ public partial class BitDialogDemo Description = "Custom CSS classes/styles for the buttons container of the BitDialog." }, new() + { + Name = "Spinner", + Type = "string?", + DefaultValue = "null", + Description = "Custom CSS classes/styles for the spinner of the ok button of the BitDialog." + }, + new() { Name = "OkButton", Type = "string?", @@ -322,6 +329,7 @@ public partial class BitDialogDemo private bool IsOpen = false; + private bool IsOpenEvent = false; private BitDialog dialogRef = default!; private BitDialog customDialogRef = default!; @@ -375,6 +383,15 @@ private void OpenDialogInPosition(BitDialogPosition positionValue) "; private readonly string example3RazorCode = @" +<BitButton OnClick=""@(() => IsOpenEvent = true)"">Open Dialog</BitButton> +<BitDialog @bind-IsOpen=""IsOpenEvent"" + Title=""Missing Subject"" + Message=""Do you want to send this message without a subject?"" + OnOk=""async () => await Task.Delay(1000)"" />"; + private readonly string example3CsharpCode = @" +private bool IsOpenEvent = false;"; + + private readonly string example4RazorCode = @" <style> .dialog-title { display: flex; @@ -441,13 +458,13 @@ Are you sure?! there's no going back. </div> </FooterTemplate> </BitDialog>"; - private readonly string example3CsharpCode = @" + private readonly string example4CsharpCode = @" private bool IsOpen2 = false; private string? optionValue; private bool IsOpen3 = false; "; - private readonly string example4RazorCode = @" + private readonly string example5RazorCode = @" <BitButton OnClick=""@(() => IsOpen4 = true)"">Open Dialog (IsBlocking = true)</BitButton> <BitButton OnClick=""@(() => IsOpen5 = true)"">Open Dialog (AutoToggleScroll = false)</BitButton> @@ -460,11 +477,11 @@ Are you sure?! there's no going back. @bind-IsOpen=""IsOpen5"" Title=""Missing Subject"" Message=""Do you want to send this message without a subject?"" />"; - private readonly string example4CsharpCode = @" + private readonly string example5CsharpCode = @" private bool IsOpen4 = false; private bool IsOpen5 = false;"; - private readonly string example5RazorCode = @" + private readonly string example6RazorCode = @" <style> .relative-container { width: 100%; @@ -530,11 +547,11 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend efficitur. </div>"; - private readonly string example5CsharpCode = @" + private readonly string example6CsharpCode = @" private bool IsOpen6 = false; private bool IsOpen7 = false;"; - private readonly string example6RazorCode = @" + private readonly string example7RazorCode = @" <BitButton OnClick=""() => OpenDialogInPosition(BitDialogPosition.TopLeft)"">Top Left</BitButton> <BitButton OnClick=""() => OpenDialogInPosition(BitDialogPosition.TopRight)"">Top Right</BitButton> <BitButton OnClick=""() => OpenDialogInPosition(BitDialogPosition.BottomLeft)"">Bottom Left</BitButton> @@ -544,7 +561,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Position=""position"" Title=""Missing Subject"" Message=""Do you want to send this message without a subject?"" />"; - private readonly string example6CsharpCode = @" + private readonly string example7CsharpCode = @" private bool IsOpenInPosition = false; private BitDialogPosition position; @@ -554,7 +571,7 @@ private void OpenDialogInPosition(BitDialogPosition positionValue) position = positionValue; }"; - private readonly string example7RazorCode = @" + private readonly string example8RazorCode = @" <style> .dialog-title { display: flex; @@ -600,13 +617,13 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitDialog>"; - private readonly string example7CsharpCode = @" + private readonly string example8CsharpCode = @" private bool IsDraggable = false; private bool IsOpen8 = false; private bool IsOpen9 = false; "; - private readonly string example8RazorCode = @" + private readonly string example9RazorCode = @" <BitButton Dir=""BitDir.Rtl"" OnClick=""@(() => IsOpen10 = true)"">باز کردن پنجره پیام</BitButton> <BitDialog @bind-IsOpen=""IsOpen10"" Dir=""BitDir.Rtl"" @@ -614,6 +631,6 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. OkText=""تایید"" CancelText=""انصراف"" Message=""آیا می خواهید این پیام را بدون موضوع ارسال کنید؟"" />"; - private readonly string example8CsharpCode = @" + private readonly string example9CsharpCode = @" private bool IsOpen10 = false;"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor index 77ea298c8f..aca6587b20 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor @@ -11,106 +11,154 @@ ComponentSubEnums="componentSubEnums"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <div> - <BitButton OnClick=@(() => IsOpen = true)>Open Modal</BitButton> - <BitModal @bind-IsOpen="IsOpen"> - <div class="modal-header"> - <span>Lorem Ipsum</span> - <BitButton Variant="BitVariant.Text" OnClick=@(() => IsOpen = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - <p> - Mauris at nunc eget lectus lobortis facilisis et eget magna. Vestibulum venenatis augue sapien, rhoncus - faucibus magna semper eget. Proin rutrum libero sagittis sapien aliquet auctor. Suspendisse tristique a - magna at facilisis. Duis rhoncus feugiat magna in rutrum. Suspendisse semper, dolor et vestibulum lacinia, - nunc felis malesuada ex, nec hendrerit justo ex et massa. Quisque quis mollis nulla. Nam commodo est ornare, - rhoncus odio eu, pharetra tellus. Nunc sed velit mi. - </p> - <p> - Sed condimentum ultricies turpis convallis pharetra. Sed sagittis quam pharetra luctus porttitor. Cras vel - consequat lectus. Sed nec fringilla urna, a aliquet libero. Aenean sed nisl purus. Vivamus vulputate felis - et odio efficitur suscipit. Ut volutpat dictum lectus, ac rutrum massa accumsan at. Sed pharetra auctor - finibus. In augue libero, commodo vitae nisi non, sagittis convallis ante. Phasellus malesuada eleifend - mollis. Curabitur ultricies leo ac metus venenatis elementum. - </p> - </div> - </BitModal> - </div> + <BitButton OnClick="() => isOpenBasic = true">Open Modal</BitButton> + <BitModal @bind-IsOpen="isOpenBasic"> + <div style="padding:1rem;max-width:40rem"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </div> + </BitModal> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Advanced options" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> + <ComponentExampleBox Title="Customizing content" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> - <div class="example-desc">BitModal has some advanced options to be customized .</div> - <div class="btn-container"> - <BitButton OnClick=@(() => IsOpen1 = true)>Open Modal (IsBlocking = true)</BitButton> - <BitButton OnClick=@(() => IsOpen2 = true)>Open Modal (AutoToggleScroll = false)</BitButton> - </div> - <div> - <BitModal @bind-IsOpen="IsOpen1" IsBlocking="true"> - <div class="modal-header"> - <span>IsBlocking = true</span> - <BitButton Variant="BitVariant.Text" OnClick=@(()=> IsOpen1 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - </div> - </BitModal> - <BitModal @bind-IsOpen="IsOpen2" AutoToggleScroll="false"> - <div class="modal-header"> - <span>AutoToggleScroll = false</span> - <BitButton Variant="BitVariant.Text" OnClick=@(()=> IsOpen2 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - </div> - </BitModal> - </div> + <BitButton OnClick="() => isOpenCustomContent = true">Open Modal</BitButton> + <BitModal @bind-IsOpen="isOpenCustomContent"> + <div class="modal-header"> + <span class="modal-header-text">Lorem Ipsum</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenCustomContent = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + <p> + Mauris at nunc eget lectus lobortis facilisis et eget magna. Vestibulum venenatis augue sapien, rhoncus + faucibus magna semper eget. Proin rutrum libero sagittis sapien aliquet auctor. Suspendisse tristique a + magna at facilisis. Duis rhoncus feugiat magna in rutrum. Suspendisse semper, dolor et vestibulum lacinia, + nunc felis malesuada ex, nec hendrerit justo ex et massa. Quisque quis mollis nulla. Nam commodo est ornare, + rhoncus odio eu, pharetra tellus. Nunc sed velit mi. + </p> + <p> + Sed condimentum ultricies turpis convallis pharetra. Sed sagittis quam pharetra luctus porttitor. Cras vel + consequat lectus. Sed nec fringilla urna, a aliquet libero. Aenean sed nisl purus. Vivamus vulputate felis + et odio efficitur suscipit. Ut volutpat dictum lectus, ac rutrum massa accumsan at. Sed pharetra auctor + finibus. In augue libero, commodo vitae nisi non, sagittis convallis ante. Phasellus malesuada eleifend + mollis. Curabitur ultricies leo ac metus venenatis elementum. + </p> + </div> + </BitModal> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Absolute positioning" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> + <ComponentExampleBox Title="Advanced options" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div class="example-desc">BitModal has an abosolute position option to further customize it's location.</div> + <div>BitModal has some advanced options.</div><br /> + <BitButton OnClick="() => isOpenBlocking = true">Open Modal (Blocking)</BitButton> + <br /><br /> + <BitButton OnClick="() => isOpenAutoToggleScroll = true">Open Modal (AutoToggleScroll)</BitButton> + <br /><br /> + <BitButton OnClick="() => isOpenModeless = true">Open Modal (Modeless)</BitButton> - <div class="btn-container"> - <BitButton OnClick=@(() => IsOpen3 = true)>Open Modal (AbsolutePosition = true)</BitButton> - <BitButton OnClick=@(() => IsOpen4 = true)>Open Modal (ScrollerSelector)</BitButton> - </div> + <BitModal @bind-IsOpen="isOpenBlocking" Blocking> + <div class="modal-header"> + <span class="modal-header-text">Blocking</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenBlocking = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + In Blocking mode, the modal won't close by clicking outside (on the overlay). + </p> + <br /> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitModal> + + <BitModal @bind-IsOpen="isOpenAutoToggleScroll" AutoToggleScroll> + <div class="modal-header"> + <span class="modal-header-text">AutoToggleScroll</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenAutoToggleScroll = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + In AutoToggleScroll mode, the scrollbar of the scroll element + (body by default and customizable with the ScrollerSelector parameter) + will be removed when the modal opens. + </p> + <br /> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitModal> + + <BitModal @bind-IsOpen="isOpenModeless" Modeless> + <div class="modal-header"> + <span class="modal-header-text">Modeless</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenModeless = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + In Modeless mode, the overlay element won't render. + </p> + <br /> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitModal> + </ExamplePreview> + </ComponentExampleBox> + <ComponentExampleBox Title="Absolute positioning" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ExamplePreview> + <div>BitModal has an absolute position option to further customize its location.</div><br /> + <BitButton OnClick="() => isOpenAbsolutePosition = true">Open Modal (AbsolutePosition)</BitButton> + <br /><br /> + <BitButton OnClick="() => isOpenScrollerSelector = true">Open Modal (ScrollerSelector)</BitButton> + <br /> <div class="relative-container"> - <BitModal @bind-IsOpen="IsOpen3" AbsolutePosition="true" AutoToggleScroll="false" IsModeless="true"> + <BitModal @bind-IsOpen="isOpenAbsolutePosition" AbsolutePosition Modeless> <div class="modal-header"> - <span>AbsolutePosition=true & IsModeless=true</span> - <BitButton Variant="BitVariant.Text" OnClick=@(()=> IsOpen3 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">AbsolutePosition & Modeless</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenAbsolutePosition = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -126,10 +174,10 @@ </div> </BitModal> - <BitModal @bind-IsOpen="IsOpen4" AbsolutePosition="true" ScrollerSelector=".relative-container"> + <BitModal @bind-IsOpen="isOpenScrollerSelector" AutoToggleScroll AbsolutePosition ScrollerSelector=".relative-container"> <div class="modal-header"> - <span>ScrollerSelector</span> - <BitButton Variant="BitVariant.Text" OnClick=@(()=> IsOpen4 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">ScrollerSelector</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenScrollerSelector = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -145,147 +193,171 @@ </div> </BitModal> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. + <div> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </div> </div> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Position" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> + <ComponentExampleBox Title="Position" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-desc">To set the modal position on the page you can use the Position parameter.</div> - <div> - <div class="position-btn"> - <div> - <BitButton OnClick="() => OpenModalInPosition(BitModalPosition.TopLeft)">Top Left</BitButton> - <BitButton OnClick="() => OpenModalInPosition(BitModalPosition.TopRight)">Top Right</BitButton> - </div> - <div> - <BitButton OnClick="() => OpenModalInPosition(BitModalPosition.BottomLeft)">Bottom Left</BitButton> - <BitButton OnClick="() => OpenModalInPosition(BitModalPosition.BottomRight)">Bottom Right</BitButton> - </div> + <div>To set the modal position on the page you can use the Position parameter.</div><br /> + <div class="position-btn-container"> + <div> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.TopLeft)">Top Left</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.TopCenter)">Top Center</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.TopRight)">Top Right</BitButton> + </div> + <div> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.CenterLeft)">Center Left</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.Center)">Center</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.CenterRight)">Center Right</BitButton> + </div> + <div> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.BottomLeft)">Bottom Left</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.BottomCenter)">Bottom Center</BitButton> + <BitButton Class="position-button" OnClick="() => OpenModalInPosition(BitModalPosition.BottomRight)">Bottom Right</BitButton> </div> - <BitModal @bind-IsOpen="IsOpenInPosition" Position="position"> - <div class="modal-header"> - <span>Modal positioning</span> - <BitButton Variant="BitVariant.Text" OnClick=@(() => IsOpenInPosition = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - BitModal with custom positioning. Lorem ipsum dolor sit amet, consectetur adipiscing elit. - </div> - </BitModal> </div> + <BitModal @bind-IsOpen="isOpenPosition" Position="position"> + <div class="modal-header"> + <span class="modal-header-text">Modal positioning</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenPosition = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + BitModal with custom positioning. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + </div> + </BitModal> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Draggable" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> + <ComponentExampleBox Title="Draggable" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> - <div> - <BitToggle Label="Is Draggable?" @bind-Value="IsDraggable" /> - <br /> - <BitButton OnClick="() => IsOpen5 = true">Open Modal</BitButton> - <BitModal @bind-IsOpen="IsOpen5" IsDraggable="IsDraggable"> - <div class="modal-header"> - <span>Draggble Modal</span> - <BitButton Variant="BitVariant.Text" OnClick=@(() => IsOpen5 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - </div> - </BitModal> - <br /> - <br /> - <br /> - <hr style="border-color:white" /> - <br /> - <br /> - <div class="example-desc">Using custom drag element.</div> - <BitButton OnClick="() => IsOpen6 = true">Open Modal</BitButton> - <BitModal @bind-IsOpen="IsOpen6" IsDraggable="true" DragElementSelector=".modal-header-drag"> - <div class="modal-header modal-header-drag"> - <span>Draggble Modal with custom drag element</span> - <BitButton Variant="BitVariant.Text" OnClick=@(() => IsOpen6 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - </p> - </div> - </BitModal> - </div> + <div>The Draggable parameter of the BitModal allows users to move the modal around the screen.</div><br /> + <BitButton OnClick="() => isOpenDraggable = true">Open Modal</BitButton> + <BitModal @bind-IsOpen="isOpenDraggable" Draggable> + <div class="modal-header"> + <span class="modal-header-text">Draggable Modal</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenDraggable = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitModal> + <br /><br /><br /><br /> + <div>Using custom drag element.</div><br /> + <BitButton OnClick="() => isOpenDraggableSelector = true">Open Modal</BitButton> + <BitModal @bind-IsOpen="isOpenDraggableSelector" Draggable DragElementSelector=".modal-header-drag"> + <div class="modal-header modal-header-drag"> + <span class="modal-header-text">Draggable Modal with custom drag element</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenDraggableSelector = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> + </BitModal> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="FullSize" RazorCode="@example7RazorCode" CSharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <div> - <div>Component's Style & Class:</div> - <br /> - <div class="btn-container"> - <BitButton OnClick="() => IsOpen7 = true">Open styled modal</BitButton> - <BitButton OnClick="() => IsOpen8 = true">Open classed modal</BitButton> + <div>The FullSize parameter of the BitModal allows the modal to be full-screen.</div><br /> + <BitButton OnClick="() => isOpenFullSize = true">Open Modal</BitButton> + <BitModal @bind-IsOpen="isOpenFullSize" FullSize="isFullSize"> + <div class="modal-header"> + <span class="modal-header-text">Full size modal</span> + <BitButton Variant="BitVariant.Text" + OnClick="() => isFullSize = !isFullSize" + IconName="@(isFullSize ? BitIconName.BackToWindow : BitIconName.ChromeFullScreen)" + Title="@(isFullSize ? "Exit FullScreen" : "FullScreen")" /> + <BitButton Variant="BitVariant.Text" + OnClick="() => isOpenFullSize = false" + IconName="@BitIconName.ChromeClose" + Title="Close" /> </div> - <br /><br /><br /> - <div><b>Styles</b> & <b>Classes</b>:</div> - <br /> - <div class="btn-container"> - <BitButton OnClick="() => IsOpen9 = true">Open modal styles</BitButton> - <BitButton OnClick="() => IsOpen10 = true">Open modal classes</BitButton> + <div class="modal-body"> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> </div> + </BitModal> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Style & Class" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8"> + <ExamplePreview> + <div> + <div>Using css related parameter users can further customize the modal based on their needs.</div> + <br /><br /> + <div>Component's Style & Class:</div><br /> + <BitButton OnClick="() => isOpenStyle = true">Open styled modal</BitButton> + <br /><br /> + <BitButton OnClick="() => isOpenClass = true">Open classed modal</BitButton> - <BitModal @bind-IsOpen="IsOpen7" Style="box-shadow: inset 0px 0px 1.5rem 1.5rem palevioletred;"> + <BitModal @bind-IsOpen="isOpenStyle" Style="box-shadow: inset 0px 0px 1.5rem 1.5rem palevioletred;"> <div class="modal-header"> - <span>Styled modal</span> - <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen7 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">Styled modal</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenStyle = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -300,11 +372,10 @@ </p> </div> </BitModal> - - <BitModal @bind-IsOpen="IsOpen8" Class="custom-class"> + <BitModal @bind-IsOpen="isOpenClass" Class="custom-class"> <div class="modal-header"> - <span>Classed modal</span> - <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen8 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">Classed modal</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenClass = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -320,10 +391,17 @@ </div> </BitModal> - <BitModal @bind-IsOpen="IsOpen9" Styles="@(new() { Overlay = "background-color: #4776f433;", Content = "box-shadow: 0 0 1rem tomato;" })"> + <br /><br /><br /><br /> + + <div><b>Styles</b> & <b>Classes</b>:</div><br /> + <BitButton OnClick="() => isOpenStyles = true">Open modal styles</BitButton> + <br /><br /> + <BitButton OnClick="() => isOpenClasses = true">Open modal classes</BitButton> + + <BitModal @bind-IsOpen="isOpenStyles" Styles="@(new() { Overlay = "background-color: #4776f433;", Content = "box-shadow: 0 0 1rem tomato;" })"> <div class="modal-header"> - <span>Modal styles</span> - <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen9 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">Modal styles</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenStyles = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -338,11 +416,10 @@ </p> </div> </BitModal> - - <BitModal @bind-IsOpen="IsOpen10" Classes="@(new() { Container = "custom-container", Overlay = "custom-overlay", Content = "custom-content" })"> + <BitModal @bind-IsOpen="isOpenClasses" Classes="@(new() { Root = "custom-root", Overlay = "custom-overlay", Content = "custom-content" })"> <div class="modal-header"> - <span>Modal classes</span> - <BitButton Variant="BitVariant.Text" OnClick="@(() => IsOpen10 = false)" IconName="@BitIconName.ChromeClose" Title="Close" /> + <span class="modal-header-text">Modal classes</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenClasses = false" IconName="@BitIconName.ChromeClose" Title="Close" /> </div> <div class="modal-body"> <p> @@ -361,67 +438,36 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Basic" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example1+7"> + <ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9"> <ExamplePreview> - <div> - <BitButton Dir="BitDir.Rtl" OnClick=@(() => IsOpen11 = true)>باز کردن مُدال</BitButton> - <BitModal Dir="BitDir.Rtl" @bind-IsOpen="IsOpen11"> - <div class="modal-header"> - <span>لورم ایپسوم</span> - <BitButton Variant="BitVariant.Text" OnClick=@(() => IsOpen11 = false) IconName="@BitIconName.ChromeClose" Title="Close" /> - </div> - <div class="modal-body"> - <p> - لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. - چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. - کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - <p> - لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. - چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. - کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - <p> - لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. - چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. - کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - </div> - </BitModal> - </div> + <div>Use BitModal in right-to-left (RTL).</div><br /> + <BitButton Dir="BitDir.Rtl" OnClick="() => isOpenRtl = true">باز کردن مُدال</BitButton> + <BitModal Dir="BitDir.Rtl" @bind-IsOpen="isOpenRtl"> + <div class="modal-header"> + <span class="modal-header-text">لورم ایپسوم</span> + <BitButton Variant="BitVariant.Text" OnClick="() => isOpenRtl = false" IconName="@BitIconName.ChromeClose" Title="Close" /> + </div> + <div class="modal-body"> + <p> + لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. + چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. + کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. + در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. + </p> + <p> + لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. + چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. + کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. + در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. + </p> + <p> + لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. + چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. + کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. + در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. + </p> + </div> + </BitModal> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> - -@code { - private bool IsOpen = false; - - private bool IsOpen1 = false; - private bool IsOpen2 = false; - - private bool IsOpen3 = false; - private bool IsOpen4 = false; - - private bool IsOpenInPosition = false; - private BitModalPosition position; - private bool IsDraggable = false; - - private bool IsOpen5 = false; - private bool IsOpen6 = false; - - private bool IsOpen7 = false; - private bool IsOpen8 = false; - private bool IsOpen9 = false; - private bool IsOpen10 = false; - - private bool IsOpen11 = false; - - private void OpenModalInPosition(BitModalPosition positionValue) - { - IsOpenInPosition = true; - position = positionValue; - } -} \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.cs index 837e89e6c1..a71094ba97 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.cs @@ -8,7 +8,7 @@ public partial class BitModalDemo { Name = "AutoToggleScroll", Type = "bool", - DefaultValue = "true", + DefaultValue = "false", Description = "Enables the auto scrollbar toggle behavior of the Modal.", }, new() @@ -19,6 +19,13 @@ public partial class BitModalDemo Description = "When true, the Modal will be positioned absolute instead of fixed.", }, new() + { + Name = "Blocking", + Type = "bool", + DefaultValue = "false", + Description = "Whether the Modal can be light dismissed by clicking outside the Modal (on the overlay).", + }, + new() { Name = "ChildContent", Type = "RenderFragment?", @@ -31,8 +38,8 @@ public partial class BitModalDemo Type = "BitModalClassStyles?", DefaultValue = "null", Description = "Custom CSS classes for different parts of the BitModal component.", + LinkType = LinkType.Link, Href = "#modal-class-styles", - LinkType = LinkType.Link }, new() { @@ -43,38 +50,52 @@ public partial class BitModalDemo }, new() { - Name = "IsAlert", - Type = "bool?", - DefaultValue = "null", - Description = "Determines the ARIA role of the dialog (alertdialog/dialog). If this is set, it will override the ARIA role determined by IsBlocking and IsModeless.", + Name = "Draggable", + Type = "bool", + DefaultValue = "false", + Description = "Whether the Modal can be dragged around.", }, new() { - Name = "IsBlocking", + Name = "FullHeight", Type = "bool", DefaultValue = "false", - Description = "Whether the dialog can be light dismissed by clicking outside the dialog (on the overlay).", + Description = "Makes the Modal height 100% of its parent container.", }, new() { - Name = "IsDraggable", + Name = "FullSize", Type = "bool", DefaultValue = "false", - Description = "Whether the Modal can be dragged around.", + Description = "Makes the Modal width and height 100% of its parent container.", }, new() { - Name = "IsModeless", + Name = "FullWidth", Type = "bool", DefaultValue = "false", - Description = "Whether the dialog should be modeless (e.g. not dismiss when focusing/clicking outside of the dialog). if true: IsBlocking is ignored, there will be no overlay.", + Description = "Makes the Modal width 100% of its parent container.", + }, + new() + { + Name = "IsAlert", + Type = "bool?", + DefaultValue = "null", + Description = "Determines the ARIA role of the Modal (alertdialog/dialog). If this is set, it will override the ARIA role determined by Blocking and Modeless.", }, new() { Name = "IsOpen", Type = "bool", DefaultValue = "false", - Description = "Whether the dialog is displayed.", + Description = "Whether the Modal is displayed.", + }, + new() + { + Name = "Modeless", + Type = "bool", + DefaultValue = "false", + Description = "Whether the Modal should be modeless (e.g. not dismiss when focusing/clicking outside of the Modal). if true: Blocking is ignored, there will be no overlay.", }, new() { @@ -85,11 +106,11 @@ public partial class BitModalDemo new() { Name = "Position", - Type = "BitModalPosition", + Type = "BitModalPosition?", + DefaultValue = "null", + Description = "Position of the Modal on the screen.", LinkType = LinkType.Link, - Href = "#component-position-enum", - DefaultValue = "BitModalPosition.Center", - Description = "Position of the modal on the screen.", + Href = "#modal-position-enum", }, new() { @@ -104,8 +125,8 @@ public partial class BitModalDemo Type = "BitModalClassStyles?", DefaultValue = "null", Description = "Custom CSS styles for different parts of the BitModal component.", + LinkType = LinkType.Link, Href = "#modal-class-styles", - LinkType = LinkType.Link }, new() { @@ -139,13 +160,6 @@ public partial class BitModalDemo Description = "Custom CSS classes/styles for the root element of the BitModal." }, new() - { - Name = "Container", - Type = "string?", - DefaultValue = "null", - Description = "Custom CSS classes/styles for the main container of the BitModal." - }, - new() { Name = "Overlay", Type = "string?", @@ -158,13 +172,6 @@ public partial class BitModalDemo Type = "string?", DefaultValue = "null", Description = "Custom CSS classes/styles for the content of the BitModal." - }, - new() - { - Name = "ScrollContent", - Type = "string?", - DefaultValue = "null", - Description = "Custom CSS classes/styles for the scroll content of the BitModal." } ] } @@ -174,7 +181,7 @@ public partial class BitModalDemo [ new() { - Id = "component-position-enum", + Id = "modal-position-enum", Name = "BitModalPosition", Description = "", Items = @@ -194,32 +201,88 @@ public partial class BitModalDemo + private bool isOpenBasic; + + private bool isOpenCustomContent; + + private bool isOpenBlocking; + private bool isOpenAutoToggleScroll; + private bool isOpenModeless; + + private bool isOpenAbsolutePosition; + private bool isOpenScrollerSelector; + + private bool isOpenPosition; + private BitModalPosition position; + private void OpenModalInPosition(BitModalPosition positionValue) + { + isOpenPosition = true; + position = positionValue; + } + + private bool isOpenDraggable; + private bool isOpenDraggableSelector; + + private bool isOpenFullSize; + private bool isFullSize; + + private bool isOpenStyle; + private bool isOpenClass; + private bool isOpenStyles; + private bool isOpenClasses; + + private bool isOpenRtl; + + private readonly string example1RazorCode = @" +<BitButton OnClick=""() => isOpenBasic = true"">Open Modal</BitButton> + +<BitModal @bind-IsOpen=""isOpenBasic""> + <div style=""padding:1rem; max-width:40rem""> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </div> +</BitModal>"; + private readonly string example1CsharpCode = @" +private bool isOpenBasic;"; + + private readonly string example2RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> -<BitButton OnClick=@(() => IsOpen = true)>Open Modal</BitButton> -<BitModal @bind-IsOpen=""IsOpen""> +<BitButton OnClick=""() => isOpenCustomContent = true"">Open Modal</BitButton> + +<BitModal @bind-IsOpen=""isOpenCustomContent""> <div class=""modal-header""> - <span>Lorem Ipsum</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(() => IsOpen = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Lorem Ipsum</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenCustomContent = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -248,38 +311,48 @@ mollis. Curabitur ultricies leo ac metus venenatis elementum. </p> </div> </BitModal>"; - private readonly string example1CsharpCode = @" -private bool IsOpen = false;"; + private readonly string example2CsharpCode = @" +private bool isOpenCustomContent;"; - private readonly string example2RazorCode = @" + private readonly string example3RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> -<BitButton OnClick=@(() => IsOpen1 = true)>Open Modal (IsBlocking = true)</BitButton> -<BitButton OnClick=@(() => IsOpen2 = true)>Open Modal (AutoToggleScroll = false)</BitButton> -<BitModal @bind-IsOpen=""IsOpen1"" IsBlocking=""true""> +<BitButton OnClick=""() => isOpenBlocking = true"">Open Modal (Blocking)</BitButton> +<BitButton OnClick=""() => isOpenAutoToggleScroll = true"">Open Modal (AutoToggleScroll)</BitButton> +<BitButton OnClick=""() => isOpenModeless = true"">Open Modal (Modeless)</BitButton> + +<BitModal @bind-IsOpen=""isOpenBlocking"" Blocking> <div class=""modal-header""> - <span>IsBlocking = true</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(()=> IsOpen1 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Blocking</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenBlocking = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> + <p> + In Blocking mode, the modal won't close by clicking outside (on the overlay). + </p> + <br /> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor @@ -293,12 +366,41 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </div> </BitModal> -<BitModal @bind-IsOpen=""IsOpen2"" AutoToggleScroll=""false""> +<BitModal @bind-IsOpen=""isOpenAutoToggleScroll"" AutoToggleScroll> <div class=""modal-header""> - <span>AutoToggleScroll = false</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(()=> IsOpen2 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">AutoToggleScroll</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenAutoToggleScroll = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> + <p> + In AutoToggleScroll mode, the scrollbar of the scroll element + (body by default and customizable with the ScrollerSelector parameter) + will be removed when the modal opens. + </p> + <br /> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> +</BitModal> + +<BitModal @bind-IsOpen=""isOpenModeless"" Modeless> + <div class=""modal-header""> + <span class=""modal-header-text"">Modeless</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenModeless = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + </div> + <div class=""modal-body""> + <p> + In Modeless mode, the overlay element won't render. + </p> + <br /> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor @@ -311,48 +413,54 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal>"; - private readonly string example2CsharpCode = @" -private bool IsOpen1 = false; -private bool IsOpen2 = false;"; + private readonly string example3CsharpCode = @" +private bool isOpenBlocking; +private bool isOpenAutoToggleScroll; +private bool isOpenModeless;"; - private readonly string example3RazorCode = @" + private readonly string example4RazorCode = @" <style> .relative-container { - margin-top: 1rem; - position: relative; width: 100%; height: 400px; - border: 2px lightgreen solid; - background-color: #eee; overflow: auto; + margin-top: 1rem; + position: relative; + background-color: #eee; + border: 2px lightgreen solid; } .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> -<BitButton OnClick=@(() => IsOpen3 = true)>Open Modal (AbsolutePosition = true)</BitButton> -<BitButton OnClick=@(() => IsOpen4 = true)>Open Modal (ScrollerSelector)</BitButton> + +<BitButton OnClick=""() => isOpenAbsolutePosition = true"">Open Modal (AbsolutePosition)</BitButton> +<BitButton OnClick=""() => isOpenScrollerSelector = true"">Open Modal (ScrollerSelector)</BitButton> <div class=""relative-container""> - <BitModal @bind-IsOpen=""IsOpen3"" AbsolutePosition=""true"" AutoToggleScroll=""false"" IsModeless=""true""> + <BitModal @bind-IsOpen=""isOpenAbsolutePosition"" AbsolutePosition Modeless> <div class=""modal-header""> - <span>AbsolutePosition=true & IsModeless=true</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(()=> IsOpen3 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">AbsolutePosition & Modeless</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenAbsolutePosition = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -367,10 +475,11 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal> - <BitModal @bind-IsOpen=""IsOpen4"" AbsolutePosition=""true"" ScrollerSelector="".relative-container""> + + <BitModal @bind-IsOpen=""isOpenScrollerSelector"" AutoToggleScroll AbsolutePosition ScrollerSelector="".relative-container""> <div class=""modal-header""> - <span>ScrollerSelector</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(()=> IsOpen4 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">ScrollerSelector</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenScrollerSelector = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -386,122 +495,127 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </div> </BitModal> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit - amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor - sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut - turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, - ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. - Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. - Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend - efficitur. + <div> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </div> </div>"; - private readonly string example3CsharpCode = @" -private bool IsOpenInPosition = false; -private BitModalPosition position; - -private void OpenModalInPosition(BitModalPosition positionValue) -{ - IsOpenInPosition = true; - position = positionValue; -}"; + private readonly string example4CsharpCode = @" +private bool isOpenAbsolutePosition; +private bool isOpenScrollerSelector;"; - private readonly string example4RazorCode = @" + private readonly string example5RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> + <BitButton OnClick=""() => OpenModalInPosition(BitModalPosition.TopLeft)"">Top Left</BitButton> <BitButton OnClick=""() => OpenModalInPosition(BitModalPosition.TopRight)"">Top Right</BitButton> <BitButton OnClick=""() => OpenModalInPosition(BitModalPosition.BottomLeft)"">Bottom Left</BitButton> <BitButton OnClick=""() => OpenModalInPosition(BitModalPosition.BottomRight)"">Bottom Right</BitButton> -<BitModal @bind-IsOpen=""IsOpenInPosition"" Position=""position""> +<BitModal @bind-IsOpen=""isOpenPosition"" Position=""position""> <div class=""modal-header""> - <span>Modal positioning</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(() => IsOpenInPosition = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Modal positioning</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenPosition = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> BitModal with custom positioning. Lorem ipsum dolor sit amet, consectetur adipiscing elit. </div> </BitModal>"; - private readonly string example4CsharpCode = @" -private bool IsOpenInPosition = false; + private readonly string example5CsharpCode = @" +private bool isOpenPosition; private BitModalPosition position; private void OpenModalInPosition(BitModalPosition positionValue) { - IsOpenInPosition = true; + isOpenPosition = true; position = positionValue; }"; - private readonly string example5RazorCode = @" + private readonly string example6RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> -<BitToggle Label=""Is Draggable?"" @bind-Value=""IsDraggable"" /> -<BitButton OnClick=""() => IsOpen5 = true"">Open Modal</BitButton> -<BitModal @bind-IsOpen=""IsOpen5"" IsDraggable=""IsDraggable""> + +<BitButton OnClick=""() => isOpenDraggable = true"">Open Modal</BitButton> +<BitModal @bind-IsOpen=""isOpenDraggable"" Draggable> <div class=""modal-header""> - <span>Draggble Modal</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(() => IsOpen5 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Draggble Modal</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenDraggable = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -517,11 +631,12 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </div> </BitModal> -<BitButton OnClick=""() => IsOpen6 = true"">Open Modal</BitButton> -<BitModal @bind-IsOpen=""IsOpen6"" IsDraggable=""true"" DragElementSelector="".modal-header-drag""> + +<BitButton OnClick=""() => isOpenDraggableSelector = true"">Open Modal</BitButton> +<BitModal @bind-IsOpen=""isOpenDraggableSelector"" Draggable DragElementSelector="".modal-header-drag""> <div class=""modal-header modal-header-drag""> - <span>Draggble Modal with custom drag element</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(() => IsOpen6 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Draggble Modal with custom drag element</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenDraggableSelector = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -536,29 +651,86 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal>"; - private readonly string example5CsharpCode = @" -private bool IsDraggable = false; -private bool IsOpen5 = false; -private bool IsOpen6 = false; -"; + private readonly string example6CsharpCode = @" +private bool isOpenDraggable; +private bool isOpenDraggableSelector;"; - private readonly string example6RazorCode = @" + private readonly string example7RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; + max-width: 960px; line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; + } +</style> + + +<BitButton OnClick=""() => isOpenFullSize = true"">Open Modal</BitButton> +<BitModal @bind-IsOpen=""isOpenFullSize"" FullSize=""isFullSize""> + <div class=""modal-header""> + <span class=""modal-header-text"">Full size modal</span> + <BitButton Variant=""BitVariant.Text"" + OnClick=""() => isFullSize = !isFullSize"" + IconName=""@(isFullSize ? BitIconName.BackToWindow : BitIconName.ChromeFullScreen)"" + Title=""@(isFullSize ? ""Exit FullScreen"" : ""FullScreen"")"" /> + <BitButton Variant=""BitVariant.Text"" + OnClick=""() => isOpenFullSize = false"" + IconName=""@BitIconName.ChromeClose"" + Title=""Close"" /> + </div> + <div class=""modal-body""> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit + amet, vulputate in leo. Maecenas vulputate congue sapien eu tincidunt. Etiam eu sem turpis. Fusce tempor + sagittis nunc, ut interdum ipsum vestibulum non. Proin dolor elit, aliquam eget tincidunt non, vestibulum ut + turpis. In hac habitasse platea dictumst. In a odio eget enim porttitor maximus. Aliquam nulla nibh, + ullamcorper aliquam placerat eu, viverra et dui. Phasellus ex lectus, maximus in mollis ac, luctus vel eros. + Vivamus ultrices, turpis sed malesuada gravida, eros ipsum venenatis elit, et volutpat eros dui et ante. + Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. Quisque pharetra eleifend + efficitur. + </p> + </div> +</BitModal>"; + private readonly string example7CsharpCode = @" +private bool isOpenFullSize; +private bool isFullSize;"; + + private readonly string example8RazorCode = @" +<style> + .modal-header { + gap: 0.5rem; + display: flex; + font-size: 24px; + font-weight: 600; + align-items: center; + padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; + } + + .modal-body { max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } .custom-class { @@ -566,7 +738,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. background-color: darkgoldenrod; } - .custom-container { + .custom-root { border: 0.25rem solid #0054C6; } @@ -583,15 +755,12 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </style> -<BitButton OnClick=""() => IsOpen7 = true"">Open styled modal</BitButton> -<BitButton OnClick=""() => IsOpen8 = true"">Open classed modal</BitButton> -<BitButton OnClick=""() => IsOpen9 = true"">Open modal styles</BitButton> -<BitButton OnClick=""() => IsOpen10 = true"">Open modal classes</BitButton> - -<BitModal @bind-IsOpen=""IsOpen7"" Style=""box-shadow: inset 0px 0px 1.5rem 1.5rem palevioletred;""> +<BitButton OnClick=""() => isOpenStyle = true"">Open styled modal</BitButton> +<BitButton OnClick=""() => isOpenClass = true"">Open classed modal</BitButton> +<BitModal @bind-IsOpen=""isOpenStyle"" Style=""box-shadow: inset 0px 0px 1.5rem 1.5rem palevioletred;""> <div class=""modal-header""> - <span>Styled modal</span> - <BitButton Variant=""BitVariant.Text"" OnClick=""@(() => IsOpen7 = false)"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Styled modal</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenStyle = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -606,11 +775,10 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal> - -<BitModal @bind-IsOpen=""IsOpen8"" Class=""custom-class""> +<BitModal @bind-IsOpen=""isOpenClass"" Class=""custom-class""> <div class=""modal-header""> - <span>Classed modal</span> - <BitButton Variant=""BitVariant.Text"" OnClick=""@(() => IsOpen8 = false)"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Classed modal</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenClass = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -626,10 +794,13 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </div> </BitModal> -<BitModal @bind-IsOpen=""IsOpen9"" Styles=""@(new() { Overlay = ""background-color: #4776f433;"", Content = ""box-shadow: 0 0 1rem tomato;"" })""> + +<BitButton OnClick=""() => isOpenStyles = true"">Open modal styles</BitButton> +<BitButton OnClick=""() => isOpenClasses = true"">Open modal classes</BitButton> +<BitModal @bind-IsOpen=""isOpenStyles"" Styles=""@(new() { Overlay = ""background-color: #4776f433;"", Content = ""box-shadow: 0 0 1rem tomato;"" })""> <div class=""modal-header""> - <span>Modal styles</span> - <BitButton Variant=""BitVariant.Text"" OnClick=""@(() => IsOpen9 = false)"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Modal styles</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenStyles = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -644,11 +815,10 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal> - -<BitModal @bind-IsOpen=""IsOpen10"" Classes=""@(new() { Container = ""custom-container"", Overlay = ""custom-overlay"", Content = ""custom-content"" })""> +<BitModal @bind-IsOpen=""isOpenClasses"" Classes=""@(new() { Root = ""custom-root"", Overlay = ""custom-overlay"", Content = ""custom-content"" })""> <div class=""modal-header""> - <span>Modal classes</span> - <BitButton Variant=""BitVariant.Text"" OnClick=""@(() => IsOpen10 = false)"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">Modal classes</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenClasses = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -663,39 +833,42 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </div> </BitModal>"; - private readonly string example6CsharpCode = @" -private bool IsOpen7 = false; -private bool IsOpen8 = false; -private bool IsOpen9 = false; -private bool IsOpen10 = false; -"; + private readonly string example8CsharpCode = @" +private bool isOpenStyle; +private bool isOpenClass; +private bool isOpenStyles; +private bool isOpenClasses;"; - private readonly string example7RazorCode = @" + private readonly string example9RazorCode = @" <style> .modal-header { + gap: 0.5rem; display: flex; - align-items: center; font-size: 24px; font-weight: 600; - border-top: 4px solid #0054C6; - justify-content: space-between; + align-items: center; padding: 12px 12px 14px 24px; + border-top: 4px solid #0054C6; + } + + .modal-header-text { + flex-grow: 1; } .modal-body { - padding: 0 24px 24px; - overflow-y: hidden; - line-height: 20px; max-width: 960px; + line-height: 20px; + overflow-y: hidden; + padding: 0 24px 24px; } </style> -<BitButton Dir=""BitDir.Rtl"" OnClick=@(() => IsOpen11 = true)>باز کردن مُدال</BitButton> -<BitModal Dir=""BitDir.Rtl"" @bind-IsOpen=""IsOpen11""> +<BitButton Dir=""BitDir.Rtl"" OnClick=""() => isOpenRtl = true"">باز کردن مُدال</BitButton> +<BitModal Dir=""BitDir.Rtl"" @bind-IsOpen=""isOpenRtl""> <div class=""modal-header""> - <span>لورم ایپسوم</span> - <BitButton Variant=""BitVariant.Text"" OnClick=@(() => IsOpen11 = false) IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> + <span class=""modal-header-text"">لورم ایپسوم</span> + <BitButton Variant=""BitVariant.Text"" OnClick=""() => isOpenRtl = false"" IconName=""@BitIconName.ChromeClose"" Title=""Close"" /> </div> <div class=""modal-body""> <p> @@ -718,6 +891,6 @@ در این صورت می توان امید داشت که تمام و دشوار </p> </div> </BitModal>"; - private readonly string example7CsharpCode = @" -private bool IsOpen11 = false;"; + private readonly string example9CsharpCode = @" +private bool isOpenRtl;"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.scss index 31f9d220a1..d25be02092 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Modal/BitModalDemo.razor.scss @@ -1,15 +1,19 @@ @import '../../../../Styles/abstracts/_functions.scss'; .modal-header { + gap: 0.5rem; display: flex; font-weight: 600; align-items: center; font-size: rem2(24px); - justify-content: space-between; border-top: rem2(4px) solid #0054C6; padding: rem2(12px) rem2(12px) rem2(14px) rem2(24px); } +.modal-header-text { + flex-grow: 1; +} + .modal-body { overflow-y: hidden; max-width: rem2(960px); @@ -17,13 +21,16 @@ padding: 0 rem2(24px) rem2(24px); } -.btn-container { - gap: 1rem; - display: flex; - flex-flow: row wrap; +.relative-container { + width: 100%; + overflow: auto; + margin-top: 1rem; + position: relative; + height: rem2(400px); + border: rem2(2px) lightgreen solid; } -.position-btn { +.position-btn-container { gap: 1rem; display: flex; flex-flow: column nowrap; @@ -34,22 +41,17 @@ } } -.relative-container { - width: 100%; - overflow: auto; - margin-top: 1rem; - height: rem2(400px); - position: relative; - border: rem2(2px) lightgreen solid; -} - ::deep { + .position-button { + width: 130px; + } + .custom-class { border: 0.5rem solid tomato; background-color: darkgoldenrod; } - .custom-container { + .custom-root { border: 0.25rem solid #0054C6; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor index b9bd4cacd0..fd58045a27 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor @@ -2,7 +2,7 @@ <PageOutlet Url="components/Panel" Title="Panel" - Description="Panel component of the bit blazorui components" /> + Description="panel component of the bit BlazorUI components" /> <div> <ComponentDemo ComponentName="Panel" @@ -12,7 +12,7 @@ ComponentSubEnums="componentSubEnums"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> - <BitButton OnClick=@(() => IsBasicPanelOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsBasicPanelOpen = true">Open Panel</BitButton> <BitPanel Classes="@panelClassStyles" @bind-IsOpen="IsBasicPanelOpen"> Content goes here. @@ -25,11 +25,11 @@ <div class="btn-container"> <div> <BitLabel>Panel with header text</BitLabel> - <BitButton OnClick=@(() => IsPanelWithHeaderTextOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsPanelWithHeaderTextOpen = true">Open Panel</BitButton> </div> <div> <BitLabel>Panel with custom header content</BitLabel> - <BitButton OnClick=@(() => IsPanelWithCustomHeaderOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsPanelWithCustomHeaderOpen = true">Open Panel</BitButton> </div> </div> @@ -74,7 +74,7 @@ <ComponentExampleBox Title="Footer" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> <BitLabel>Panel with custom footer content</BitLabel> - <BitButton OnClick=@(() => IsPanelWithFooterOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsPanelWithFooterOpen = true">Open Panel</BitButton> <BitPanel Classes="@(new() { Header = "header-margin", Footer = "footer-margin" })" HeaderText="BitPanel with custom footer content" @bind-IsOpen="IsPanelWithFooterOpen"> <ChildContent> @@ -90,8 +90,8 @@ </p> </ChildContent> <FooterTemplate> - <BitButton OnClick=@(() => IsPanelWithFooterOpen = false)>Save</BitButton> - <BitButton Variant="BitVariant.Outline" OnClick=@(() => IsPanelWithFooterOpen = false)>Close</BitButton> + <BitButton OnClick="() => IsPanelWithFooterOpen = false">Save</BitButton> + <BitButton Variant="BitVariant.Outline" OnClick="() => IsPanelWithFooterOpen = false">Close</BitButton> </FooterTemplate> </BitPanel> </ExamplePreview> @@ -99,20 +99,21 @@ <ComponentExampleBox Title="Advanced options" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> - <div class="example-desc">BitPanel has some advanced options to be customized.</div> + <div>BitPanel has some advanced options to be customized.</div> + <br /><br /> <div> <div class="btn-container"> <div> <BitLabel>Panel with IsBlocking = true</BitLabel> - <BitButton OnClick=@(() => IsBlockingPanelOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsBlockingPanelOpen = true">Open Panel</BitButton> </div> <div> <BitLabel>Panel with IsModeless = true</BitLabel> - <BitButton OnClick=@(() => IsModelessPanelOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsModelessPanelOpen = true">Open Panel</BitButton> </div> <div> <BitLabel>Panel with AutoToggleScroll = false</BitLabel> - <BitButton OnClick=@(() => IsAutoToggleScrollPanelOpen = true)>Open Panel</BitButton> + <BitButton OnClick="() => IsAutoToggleScrollPanelOpen = true">Open Panel</BitButton> </div> <div> <BitLabel>Panel with ShowCloseButton = false</BitLabel> @@ -154,40 +155,43 @@ <ComponentExampleBox Title="Position and size" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-desc">To set the Panel position on the page you can use the Position parameter.</div> - <div class="position-btn"> - <BitSpinButton @bind-Value="CustomPanelSize" Mode="BitSpinButtonMode.Inline" Label="Custom size" /> - <div> + <div>To set the Panel position on the page you can use the Position parameter.</div> + <br /><br /> + <div> + <div class="position-btn"> + <BitSpinButton @bind-Value="CustomPanelSize" Mode="BitSpinButtonMode.Inline" Label="Custom size" /> + <div> <BitButton OnClick="() => OpenPanelInPosition(BitPanelPosition.Left)">Left</BitButton> <BitButton OnClick="() => OpenPanelInPosition(BitPanelPosition.Right)">Right</BitButton> - </div> - <div> + </div> + <div> <BitButton OnClick="() => OpenPanelInPosition(BitPanelPosition.Top)">Top</BitButton> <BitButton OnClick="() => OpenPanelInPosition(BitPanelPosition.Bottom)">Bottom</BitButton> + </div> </div> + <BitPanel Classes="@panelClassStyles" @bind-Size="CustomPanelSize" HeaderText="Panel types" @bind-IsOpen="IsOpenInPosition" Position="position"> + <p> + BitPanel with custom position and size. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + </p> + <BitSpinButton @bind-Value="CustomPanelSize" Mode="BitSpinButtonMode.Inline" Label="Custom size" /> + </BitPanel> </div> - <BitPanel Classes="@panelClassStyles" @bind-Size="CustomPanelSize" HeaderText="Panel types" @bind-IsOpen="IsOpenInPosition" Position="position"> - <p> - BitPanel with custom position and size. Lorem ipsum dolor sit amet, consectetur adipiscing elit. - </p> - <BitSpinButton @bind-Value="CustomPanelSize" Mode="BitSpinButtonMode.Inline" Label="Custom size" /> - </BitPanel> </ExamplePreview> </ComponentExampleBox> <ComponentExampleBox Title="Style & Class" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> + <div>Explore styling and class customization for BitPanel, including component styles, custom classes, and detailed styles.</div> + <br /><br /> <div> <div class="btn-container"> <div>Component's Style & Class:</div> - <br /> <BitButton OnClick="() => IsStyledPanelOpen = true">Open styled panel</BitButton> <BitButton OnClick="() => IsClassedPanelOpen = true">Open classed panel</BitButton> </div> <br /><br /><br /> <div class="btn-container"> <div><b>Styles</b> & <b>Classes</b>:</div> - <br /> <BitButton OnClick="() => IsPanelStylesOpen = true">Open panel styles</BitButton> <BitButton OnClick="() => IsPanelClassesOpen = true">Open panel classes</BitButton> </div> @@ -221,58 +225,26 @@ <ComponentExampleBox Title="RTL" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7"> <ExamplePreview> - <BitButton Dir="BitDir.Rtl" OnClick=@(() => IsRtlPanelOpen = true)> - باز کردن پنل - </BitButton> - - <BitPanel @bind-IsOpen="IsRtlPanelOpen" - Dir="BitDir.Rtl" - HeaderText="سرصفحه ی ساده" - Classes="@panelClassStyles"> - <p> - لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. - چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. - کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. - در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. - </p> - </BitPanel> + <div>Use BitPanel in right-to-left (RTL).</div> + <br /><br /> + <div> + <BitButton Dir="BitDir.Rtl" OnClick="() => IsRtlPanelOpen = true"> + باز کردن پنل + </BitButton> + + <BitPanel @bind-IsOpen="IsRtlPanelOpen" + Dir="BitDir.Rtl" + HeaderText="سرصفحه ی ساده" + Classes="@panelClassStyles"> + <p> + لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. + چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. + کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. + در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. + </p> + </BitPanel> + </div> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> -</div> - -@code { - private BitPanelClassStyles panelClassStyles = new() { Header = "header-margin" }; - - private bool IsBasicPanelOpen = false; - - private bool IsPanelWithHeaderTextOpen = false; - private bool IsPanelWithCustomHeaderOpen = false; - - private bool IsPanelWithFooterOpen = false; - - private bool IsBlockingPanelOpen = false; - private bool IsModelessPanelOpen = false; - private bool IsAutoToggleScrollPanelOpen = false; - - private bool IsOpenInPosition = false; - - private bool IsStyledPanelOpen = false; - private bool IsClassedPanelOpen = false; - private bool IsPanelStylesOpen = false; - private bool IsPanelClassesOpen = false; - - private bool IsRtlPanelOpen = false; - - private BitPanel bitPanelRef = default!; - - private double CustomPanelSize = 320; - - private BitPanelPosition position; - - private void OpenPanelInPosition(BitPanelPosition positionValue) - { - IsOpenInPosition = true; - position = positionValue; - } -} \ No newline at end of file +</div> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor.cs index e41e653bd8..96d7f8169b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Panel/BitPanelDemo.razor.cs @@ -8,7 +8,7 @@ public partial class BitPanelDemo { Name = "AutoToggleScroll", Type = "bool", - DefaultValue = "true", + DefaultValue = "false", Description = "Enables the auto scrollbar toggle behavior of the Panel.", }, new() @@ -224,8 +224,44 @@ public partial class BitPanelDemo + private BitPanelClassStyles panelClassStyles = new() { Header = "header-margin" }; + + private bool IsBasicPanelOpen = false; + + private bool IsPanelWithHeaderTextOpen = false; + private bool IsPanelWithCustomHeaderOpen = false; + + private bool IsPanelWithFooterOpen = false; + + private bool IsBlockingPanelOpen = false; + private bool IsModelessPanelOpen = false; + private bool IsAutoToggleScrollPanelOpen = false; + + private bool IsOpenInPosition = false; + + private bool IsStyledPanelOpen = false; + private bool IsClassedPanelOpen = false; + private bool IsPanelStylesOpen = false; + private bool IsPanelClassesOpen = false; + + private bool IsRtlPanelOpen = false; + + private BitPanel bitPanelRef = default!; + + private double CustomPanelSize = 320; + + private BitPanelPosition position; + + private void OpenPanelInPosition(BitPanelPosition positionValue) + { + IsOpenInPosition = true; + position = positionValue; + } + + + private readonly string example1RazorCode = @" -<BitButton OnClick=@(() => IsBasicPanelOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsBasicPanelOpen = true"">Open Panel</BitButton> <BitPanel @bind-IsOpen=""IsBasicPanelOpen""> Content goes here. @@ -235,10 +271,10 @@ Content goes here. private readonly string example2RazorCode = @" <BitLabel>Panel with header text</BitLabel> -<BitButton OnClick=@(() => IsPanelWithHeaderTextOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsPanelWithHeaderTextOpen = true"">Open Panel</BitButton> <BitLabel>Panel with custom header content</BitLabel> -<BitButton OnClick=@(() => IsPanelWithCustomHeaderOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsPanelWithCustomHeaderOpen = true"">Open Panel</BitButton> <BitPanel HeaderText=""Simple header"" @bind-IsOpen=""IsPanelWithHeaderTextOpen""> <p> @@ -281,7 +317,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. private readonly string example3RazorCode = @" <BitLabel>Panel with custom footer content</BitLabel> -<BitButton OnClick=@(() => IsPanelWithFooterOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsPanelWithFooterOpen = true"">Open Panel</BitButton> <BitPanel Title=""BitPanel with custom footer content"" @bind-IsOpen=""IsPanelWithFooterOpen""> <ChildContent> @@ -297,8 +333,8 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. </p> </ChildContent> <FooterTemplate> - <BitButton OnClick=@(() => IsPanelWithFooterOpen = false)>Save</BitButton> - <BitButton Variant=""BitVariant.Outline"" OnClick=@(() => IsPanelWithFooterOpen = false)>Close</BitButton> + <BitButton OnClick=""() => IsPanelWithFooterOpen = false"">Save</BitButton> + <BitButton Variant=""BitVariant.Outline"" OnClick=""() => IsPanelWithFooterOpen = false"">Close</BitButton> </FooterTemplate> </BitPanel>"; private readonly string example3CsharpCode = @" @@ -306,13 +342,13 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. private readonly string example4RazorCode = @" <BitLabel>Panel with IsBlocking = true</BitLabel> -<BitButton OnClick=@(() => IsBlockingPanelOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsBlockingPanelOpen = true"">Open Panel</BitButton> <BitLabel>Panel with IsModeless = true</BitLabel> -<BitButton OnClick=@(() => IsModelessPanelOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsModelessPanelOpen = true"">Open Panel</BitButton> <BitLabel>Panel with AutoToggleScroll = false</BitLabel> -<BitButton OnClick=@(() => IsAutoToggleScrollPanelOpen = true)>Open Panel</BitButton> +<BitButton OnClick=""() => IsAutoToggleScrollPanelOpen = true"">Open Panel</BitButton> <BitLabel>Panel with ShowCloseButton = false</BitLabel> <BitButton OnClick=""() => bitPanelRef.Open()"">Open Panel</BitButton> @@ -324,6 +360,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. sagittis nunc, ut interdum ipsum vestibulum non. </p> </BitPanel> + <BitPanel HeaderText=""IsModeless = true"" @bind-IsOpen=""IsModelessPanelOpen"" IsModeless=""true""> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit @@ -331,6 +368,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. sagittis nunc, ut interdum ipsum vestibulum non. </p> </BitPanel> + <BitPanel HeaderText=""AutoToggleScroll = false"" @bind-IsOpen=""IsAutoToggleScrollPanelOpen"" AutoToggleScroll=""false""> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit @@ -338,6 +376,7 @@ Quisque ultricies mi nec leo ultricies mollis. Vivamus egestas volutpat lacinia. sagittis nunc, ut interdum ipsum vestibulum non. </p> </BitPanel> + <BitPanel @ref=""bitPanelRef"" HeaderText=""ShowCloseButton = false"" ShowCloseButton=""false""> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lorem nulla, malesuada ut sagittis sit @@ -446,7 +485,7 @@ Content goes here. private bool IsPanelClassesOpen = false;"; private readonly string example7RazorCode = @" -<BitButton Dir=""BitDir.Rtl"" OnClick=@(() => IsRtlPanelOpen = true)> +<BitButton Dir=""BitDir.Rtl"" OnClick=""() => IsRtlPanelOpen = true""> باز کردن پنل </BitButton> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor index 204d0dd844..53755fa255 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor @@ -11,9 +11,7 @@ ComponentSubClasses="componentSubClasses"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <div> - Explore the fundamental use of BitTooltip with a simple hover interaction on a BitButton. - </div> + <div>Explore the fundamental use of BitTooltip with a simple hover interaction on a BitButton.</div> <br /> <div class="tooltips-container"> <BitTooltip Text="This is the tooltip text"> @@ -25,9 +23,7 @@ <ComponentExampleBox Title="Position" RazorCode="@example2RazorCode" Id="example2"> <ExamplePreview> - <div> - You can customize the Tooltip position to enhance the user experience. - </div> + <div>You can customize the Tooltip position to enhance the user experience.</div> <br /> <div class="tooltips-container center"> <div class="tooltips-container"> @@ -51,40 +47,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example3RazorCode" Id="example3"> - <ExamplePreview> - <div> - Empower customization by overriding default styles and classes, allowing tailored design modifications to suit specific UI requirements. - </div> - <br /><br /> - <div>Component's Style & Class:</div><br /> - <div class="tooltips-container"> - <BitTooltip Text="This is the tooltip text" Class="custom-class"> - <div class="custom-content"> - <div>Item 1</div> - <div>Item 2</div> - <div>Item 3</div> - </div> - </BitTooltip> - </div> - <br /><br /><br /> - <div><b>Styles</b> & <b>Classes</b>:</div><br /> - <div class="tooltips-container"> - <BitTooltip Text="This is the tooltip text" Styles="@(new() { Tooltip = "box-shadow: aqua 0 0 0.5rem;" })"> - <BitButton Variant="BitVariant.Outline">Hover over me</BitButton> - </BitTooltip> - <br /><br /> - <BitTooltip Text="This is the tooltip text" - Classes="@(new() { Root = "custom-root", - Tooltip = "custom-tooltip", - Arrow = "custom-arrow" })"> - <BitButton Variant="BitVariant.Outline">Hover over me</BitButton> - </BitTooltip> - </div> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Custom content" RazorCode="@example4RazorCode" Id="example4"> + <ComponentExampleBox Title="Custom content" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> <div>Here are some examples of customizing the tooltip content.</div> <br /> @@ -104,26 +67,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example5RazorCode" Id="example5"> - <ExamplePreview> - <br /><br /> - <div class="tooltips-container"> - <BitTooltip Dir="BitDir.Rtl"> - <Template> - <ul style="padding: 0.5rem; margin: 0;"> - <li>۱. یک</li> - <li>۲. دو</li> - </ul> - </Template> - <Anchor> - <BitButton Variant="BitVariant.Outline">نشانگر ماوس را روی من بیاورید</BitButton> - </Anchor> - </BitTooltip> - </div> - </ExamplePreview> - </ComponentExampleBox> - - <ComponentExampleBox Title="Advanced" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> + <ComponentExampleBox Title="Advanced" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> <div>Here are some examples of advanced usage of tooltip.</div> <br /> @@ -145,16 +89,53 @@ </div> <div class="tooltips-container"> - <BitToggle @bind-Value="isShown" DefaultText="Toggle tooltip state" /> + <BitToggle @bind-Value="isShown" Text="Toggle tooltip state" /> <br /> - <BitToggle @bind-Value="hideArrow" DefaultText="Toggle tooltip arrow" /> + <BitToggle @bind-Value="hideArrow" Text="Hide tooltip arrow" /> <br /> - <BitToggle @bind-Value="showOnClick" DefaultText="Show tooltip on click" /> + <BitToggle @bind-Value="showOnClick" Text="Show tooltip on click" /> <br /> - <BitToggle @bind-Value="showOnHover" DefaultText="Show tooltip on hover" /> + <BitToggle @bind-Value="showOnHover" Text="Show tooltip on hover" /> </div> </div> </ExamplePreview> </ComponentExampleBox> + <ComponentExampleBox Title="Styles & Classes" RazorCode="@example5RazorCode" Id="example5"> + <ExamplePreview> + <div>Empower customization by overriding default styles and classes, allowing tailored design modifications to suit specific UI requirements.</div> + <br /><br /> + <div class="tooltips-container"> + <div><b>Styles</b>:</div> + <BitTooltip Text="This is the tooltip text" Styles="@(new() { Tooltip = "box-shadow: aqua 0 0 0.5rem;" })"> + <BitButton Variant="BitVariant.Outline">Hover over me</BitButton> + </BitTooltip> + <br /><br /> + <div><b>Classes</b>:</div> + <BitTooltip Text="This is the tooltip text" Classes="@(new() { Tooltip = "custom-tooltip", Arrow = "custom-arrow" })"> + <BitButton Variant="BitVariant.Outline">Hover over me</BitButton> + </BitTooltip> + </div> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="RTL" RazorCode="@example6RazorCode" Id="example6"> + <ExamplePreview> + <div>Use BitTooltip in right-to-left (RTL).</div> + <br /><br /> + <div class="tooltips-container"> + <BitTooltip Dir="BitDir.Rtl"> + <Template> + <ul style="padding: 0.5rem; margin: 0;"> + <li>۱. یک</li> + <li>۲. دو</li> + </ul> + </Template> + <Anchor> + <BitButton Variant="BitVariant.Outline">نشانگر ماوس را روی من بیاورید</BitButton> + </Anchor> + </BitTooltip> + </div> + </ExamplePreview> + </ComponentExampleBox> </ComponentDemo> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.cs index dafa0f2360..72caf5b122 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.cs @@ -252,7 +252,7 @@ public partial class BitTooltipDemo private BitTooltipPosition tooltipPosition; - private List<BitDropdownItem<BitTooltipPosition>> tooltipPositionList = Enum.GetValues(typeof(BitTooltipPosition)) + private readonly List<BitDropdownItem<BitTooltipPosition>> tooltipPositionList = Enum.GetValues(typeof(BitTooltipPosition)) .Cast<BitTooltipPosition>() .Select(enumValue => new BitDropdownItem<BitTooltipPosition> { @@ -289,54 +289,6 @@ public partial class BitTooltipDemo </BitTooltip>"; private readonly string example3RazorCode = @" -<style> - .custom-class .custom-content { - gap: 0.5rem; - padding: 0.5rem; - color: blueviolet; - border-radius: 1rem; - display: inline-flex; - box-shadow: aqua 0 0 0.5rem; - } - - .custom-root { - text-shadow: aqua 0 0 0.5rem; - } - - .custom-tooltip { - color: tomato; - border: solid tomato; - border-radius: 0.5rem; - } - - .custom-arrow { - border-right: solid tomato; - border-bottom: solid tomato; - } -</style> - - -<BitTooltip Text=""This is the tooltip text"" Class=""custom-class""> - <div class=""custom-content""> - <div>Item 1</div> - <div>Item 2</div> - <div>Item 3</div> - </div> -</BitTooltip> - - -<BitTooltip Text=""This is the tooltip text"" Styles=""@(new() { Tooltip = ""box-shadow: aqua 0 0 0.5rem;"" })""> - <BitButton Variant=""BitVariant.Outline"">Hover over me</BitButton> -</BitTooltip> - -<BitTooltip Text=""This is the tooltip text"" - Classes=""@(new() { Root = ""custom-root"", - Tooltip = ""custom-tooltip"", - Arrow = ""custom-arrow"" })""> - <BitButton Variant=""BitVariant.Outline"">Hover over me</BitButton> -</BitTooltip>"; - - private readonly string example4RazorCode = @" <BitTooltip> <Template> <ul style=""padding: 0.5rem; margin: 0;""> @@ -349,20 +301,7 @@ public partial class BitTooltipDemo </Anchor> </BitTooltip>"; - private readonly string example5RazorCode = @" -<BitTooltip Dir=""BitDir.Rtl""> - <Template> - <ul style=""padding: 0.5rem; margin: 0;""> - <li>۱. یک</li> - <li>۲. دو</li> - </ul> - </Template> - <Anchor> - <BitButton Variant=""BitVariant.Outline"">نشانگر ماوس را روی من بیاورید</BitButton> - </Anchor> -</BitTooltip>"; - - private readonly string example6RazorCode = @" + private readonly string example4RazorCode = @" <BitTooltip @bind-IsShown=""isShown"" Text=""Text"" HideArrow=""hideArrow"" @@ -375,11 +314,11 @@ public partial class BitTooltipDemo <BitDropdown Label=""Tooltip positions"" Items=""tooltipPositionList"" @bind-Value=""tooltipPosition"" /> <BitSpinButton Label=""Hide delay"" @bind-Value=""hideDelay"" Mode=""BitSpinButtonMode.Inline"" /> -<BitToggle @bind-Value=""isShown"" DefaultText=""Toggle tooltip state"" /> -<BitToggle @bind-Value=""hideArrow"" DefaultText=""Toggle tooltip arrow"" /> -<BitToggle @bind-Value=""showOnClick"" DefaultText=""Show tooltip on click"" /> -<BitToggle @bind-Value=""showOnHover"" DefaultText=""Show tooltip on hover"" />"; - private readonly string example6CsharpCode = @" +<BitToggle @bind-Value=""isShown"" Text=""Toggle tooltip state"" /> +<BitToggle @bind-Value=""hideArrow"" Text=""Hide tooltip arrow"" /> +<BitToggle @bind-Value=""showOnClick"" Text=""Show tooltip on click"" /> +<BitToggle @bind-Value=""showOnHover"" Text=""Show tooltip on hover"" />"; + private readonly string example4CsharpCode = @" private bool isShown = true; private bool showOnClick = true; private bool showOnHover; @@ -388,7 +327,7 @@ public partial class BitTooltipDemo private BitTooltipPosition tooltipPosition; -private List<BitDropdownItem<BitTooltipPosition>> tooltipPositionList = Enum.GetValues(typeof(BitTooltipPosition)) +private readonly List<BitDropdownItem<BitTooltipPosition>> tooltipPositionList = Enum.GetValues(typeof(BitTooltipPosition)) .Cast<BitTooltipPosition>() .Select(enumValue => new BitDropdownItem<BitTooltipPosition> { @@ -396,4 +335,40 @@ public partial class BitTooltipDemo Text = enumValue.ToString() }) .ToList();"; + + private readonly string example5RazorCode = @" +<style> + .custom-tooltip { + color: tomato; + border: solid tomato; + border-radius: 0.5rem; + } + + .custom-arrow { + border-right: solid tomato; + border-bottom: solid tomato; + } +</style> + + +<BitTooltip Text=""This is the tooltip text"" Styles=""@(new() { Tooltip = ""box-shadow: aqua 0 0 0.5rem;"" })""> + <BitButton Variant=""BitVariant.Outline"">Hover over me</BitButton> +</BitTooltip> + +<BitTooltip Text=""This is the tooltip text"" Classes=""@(new() { Tooltip = ""custom-tooltip"", Arrow = ""custom-arrow"" })""> + <BitButton Variant=""BitVariant.Outline"">Hover over me</BitButton> +</BitTooltip>"; + + private readonly string example6RazorCode = @" +<BitTooltip Dir=""BitDir.Rtl""> + <Template> + <ul style=""padding: 0.5rem; margin: 0;""> + <li>۱. یک</li> + <li>۲. دو</li> + </ul> + </Template> + <Anchor> + <BitButton Variant=""BitVariant.Outline"">نشانگر ماوس را روی من بیاورید</BitButton> + </Anchor> +</BitTooltip>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.scss index 387b835ca3..57fe937c0e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Surfaces/Tooltip/BitTooltipDemo.razor.scss @@ -18,19 +18,6 @@ } ::deep { - .custom-class .custom-content { - gap: 0.5rem; - padding: 0.5rem; - color: blueviolet; - border-radius: 1rem; - display: inline-flex; - box-shadow: aqua 0 0 0.5rem; - } - - .custom-root { - text-shadow: aqua 0 0 0.5rem; - } - .custom-tooltip { color: tomato; border: solid tomato; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor index 6f06bf397e..7f172aa2fc 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor @@ -12,8 +12,10 @@ <BitElement>This is default (div)</BitElement> </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Tag" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> + <ComponentExampleBox Title="Element" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> + <div>Render different HTML elements by setting the Element property of BitElement.</div> + <br /> <BitElement Element="input" placeholder="Input" /> <br /><br /> <BitElement Element="a" href="https://bitplatform.dev/" target="_blank">Anchor (Link)</BitElement> @@ -23,14 +25,15 @@ </ComponentExampleBox> <ComponentExampleBox Title="Advanced" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <BitElement Element="@elementTag" - placeholder="@elementTag" + <div>Change the HTML tag dynamically using the Element property.</div> + <br /><br /> + <BitElement Element="@element" + placeholder="@element" target="_blank" href="https://bitplatform.dev/"> - @elementTag + @element </BitElement> - <br /><br /> - <BitDropdown Label="Tags" Items="tagsList" @bind-Value="elementTag" Style="width: 8rem;" /> + <BitDropdown Label="Elements" Items="elementsList" @bind-Value="element" Style="width: 8rem; margin-top: 0.5rem;" /> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor.cs index c62be25210..245f69d64b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Element/BitElementDemo.razor.cs @@ -24,8 +24,8 @@ public partial class BitElementDemo private int counter; - private string elementTag = "div"; - private List<BitDropdownItem<string>> tagsList = + private string element = "div"; + private List<BitDropdownItem<string>> elementsList = [ new() { Text = "div", Value = "div" }, new() { Text = "a", Value = "a" }, @@ -41,24 +41,24 @@ public partial class BitElementDemo <BitElement>This is default (div)</BitElement>"; private string example2RazorCode = @" -<BitElement Tag=""input"" placeholder=""Input"" /> -<BitElement Tag=""a"" href=""https://bitplatform.dev/"" target=""_blank"">Anchor (Link)</BitElement> -<BitElement Tag=""button"" @onclick=""@(() => counter++)"">Button (Click count @counter)</BitElement>"; +<BitElement Element=""input"" placeholder=""Input"" /> +<BitElement Element=""a"" href=""https://bitplatform.dev/"" target=""_blank"">Anchor (Link)</BitElement> +<BitElement Element=""button"" @onclick=""@(() => counter++)"">Button (Click count @counter)</BitElement>"; private string example2CsharpCode = @" private int counter;"; private string example3RazorCode = @" -<BitElement Tag=""@elementTag"" - placeholder=""@elementTag"" +<BitElement Element=""@element"" + placeholder=""@element"" target=""_blank"" href=""https://bitplatform.dev/""> - @elementTag + @element </BitElement> -<BitDropdown Label=""Tags"" Items=""tagsList"" @bind-Value=""elementTag"" />"; +<BitDropdown Label=""Elements"" Items=""elementsList"" @bind-Value=""element"" />"; private string example3CsharpCode = @" -private string elementTag = ""div""; -private List<BitDropdownItem<string>> tagsList = new() +private string element = ""div""; +private List<BitDropdownItem<string>> elementsList = new() { new() { Text = ""div"", Value = ""div"" }, new() { Text = ""a"", Value = ""a"" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor index 68f83cef2a..00fe7d7602 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor @@ -25,7 +25,20 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Size" RazorCode="@example2RazorCode" Id="example2"> + <ComponentExampleBox Title="Variant" RazorCode="@example2RazorCode" Id="example2"> + <ExamplePreview> + <div>Fill</div><br /> + <BitIcon IconName="@BitIconName.Accept" Variant="BitVariant.Fill" /> + <br /><br /><br /><br /> + <div>Outline</div><br /> + <BitIcon IconName="@BitIconName.Accept" Variant="BitVariant.Outline" /> + <br /><br /><br /><br /> + <div>Text (default)</div><br /> + <BitIcon IconName="@BitIconName.Accept" Variant="BitVariant.Text" /> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Size" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> <div>Small</div><br /> <BitIcon Size="BitSize.Small" IconName="@BitIconName.Accept" /> @@ -50,7 +63,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Color" RazorCode="@example3RazorCode" Id="example3"> + <ComponentExampleBox Title="Color" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> <div>Offering a range of specialized color variants, providing visual cues for specific actions or states within your application.</div> <br /><br /><br /> @@ -112,7 +125,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example4RazorCode" Id="example4"> + <ComponentExampleBox Title="Style & Class" RazorCode="@example5RazorCode" Id="example5"> <ExamplePreview> <div> <BitIcon IconName="@BitIconName.Accept" Size="BitSize.Large" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs index f0026b260d..28f1cedbab 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs @@ -31,6 +31,15 @@ public partial class BitIconDemo LinkType = LinkType.Link, Href = "#icon-size-enum", }, + new() + { + Name = "Variant", + Type = "BitVariant?", + DefaultValue = "null", + Description = "The visual variant of the icon.", + LinkType = LinkType.Link, + Href = "#variant-enum", + }, ]; private readonly List<ComponentSubEnum> componentSubEnums = @@ -89,6 +98,60 @@ public partial class BitIconDemo Name= "Error", Description="Error general color.", Value="7", + }, + new() + { + Name= "PrimaryBackground", + Description="Primary background color.", + Value="8", + }, + new() + { + Name= "SecondaryBackground", + Description="Secondary background color.", + Value="9", + }, + new() + { + Name= "TertiaryBackground", + Description="Tertiary background color.", + Value="10", + }, + new() + { + Name= "PrimaryForeground", + Description="Primary foreground color.", + Value="11", + }, + new() + { + Name= "SecondaryForeground", + Description="Secondary foreground color.", + Value="12", + }, + new() + { + Name= "TertiaryForeground", + Description="Tertiary foreground color.", + Value="13", + }, + new() + { + Name= "PrimaryBorder", + Description="Primary border color.", + Value="14", + }, + new() + { + Name= "SecondaryBorder", + Description="Secondary border color.", + Value="15", + }, + new() + { + Name= "TertiaryBorder", + Description="Tertiary border color.", + Value="16", } ] }, @@ -118,6 +181,33 @@ public partial class BitIconDemo Value = "2", } ] + }, + new() + { + Id = "variant-enum", + Name = "BitVariant", + Description = "Determines the variant of the content that controls the rendered style of the corresponding element(s).", + Items = + [ + new() + { + Name = "Fill", + Description = "Fill styled variant.", + Value = "0", + }, + new() + { + Name = "Outline", + Description = "Outline styled variant.", + Value = "1", + }, + new() + { + Name = "Text", + Description = "Text styled variant.", + Value = "2", + } + ] } ]; @@ -133,6 +223,13 @@ public partial class BitIconDemo <BitIcon IconName=""@BitIconName.Pinned"" IsEnabled=""false"" />"; private readonly string example2RazorCode = @" +<BitIcon IconName=""@BitIconName.Accept"" Variant=""BitVariant.Fill"" /> + +<BitIcon IconName=""@BitIconName.Accept"" Variant=""BitVariant.Outline"" /> + +<BitIcon IconName=""@BitIconName.Accept"" Variant=""BitVariant.Text"" />"; + + private readonly string example3RazorCode = @" <BitIcon Size=""BitSize.Small"" IconName=""@BitIconName.Accept"" /> <BitIcon Size=""BitSize.Small"" IconName=""@BitIconName.Bus"" /> <BitIcon Size=""BitSize.Small"" IconName=""@BitIconName.Pinned"" /> @@ -145,7 +242,7 @@ public partial class BitIconDemo <BitIcon Size=""BitSize.Large"" IconName=""@BitIconName.Bus"" /> <BitIcon Size=""BitSize.Large"" IconName=""@BitIconName.Pinned"" />"; - private readonly string example3RazorCode = @" + private readonly string example4RazorCode = @" <BitIcon Color=""BitColor.Primary"" IconName=""@BitIconName.Accept"" /> <BitIcon Color=""BitColor.Primary"" IconName=""@BitIconName.Bus"" /> <BitIcon Color=""BitColor.Primary"" IconName=""@BitIconName.Pinned"" /> @@ -178,7 +275,7 @@ public partial class BitIconDemo <BitIcon Color=""BitColor.Error"" IconName=""@BitIconName.Bus"" /> <BitIcon Color=""BitColor.Error"" IconName=""@BitIconName.Pinned"" />"; - private readonly string example4RazorCode = @" + private readonly string example5RazorCode = @" <style> .icon-class { padding: 4px; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor index 8cbcba5868..29f5b72545 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor @@ -7,7 +7,8 @@ <ComponentDemo ComponentName="Link" ComponentDescription="Links lead to another part of an app, other pages, or help articles. They can also be used to initiate commands." ComponentParameters="componentParameters" - ComponentSubClasses="componentSubClasses"> + ComponentSubClasses="componentSubClasses" + ComponentSubEnums="componentSubEnums"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> <BitLink Href="https://github.com/bitfoundation/bitplatform">Basic Link</BitLink> @@ -95,7 +96,15 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="Style & Class" RazorCode="@example7RazorCode" Id="example7"> + <ComponentExampleBox Title="Rel" RazorCode="@example7RazorCode" Id="example7"> + <ExamplePreview> + <BitLink Rel="BitAnchorRel.NoFollow" Href="https://github.com/bitfoundation/bitplatform">Link with a rel attribute (nofollow)</BitLink> + <br /><br /> + <BitLink Rel="BitAnchorRel.NoFollow | BitAnchorRel.NoReferrer" Href="https://github.com/bitfoundation/bitplatform">Link with a rel attribute (nofollow & noreferrer)</BitLink> + </ExamplePreview> + </ComponentExampleBox> + + <ComponentExampleBox Title="Style & Class" RazorCode="@example8RazorCode" Id="example8"> <ExamplePreview> <BitLink Style="color: goldenrod; font-weight:bold" Href="https://github.com/bitfoundation/bitplatform">Link with style</BitLink> <br /><br /> @@ -103,7 +112,7 @@ </ExamplePreview> </ComponentExampleBox> - <ComponentExampleBox Title="RTL" RazorCode="@example8RazorCode" Id="example8"> + <ComponentExampleBox Title="RTL" RazorCode="@example9RazorCode" Id="example9"> <ExamplePreview> <div dir="rtl"> <BitLink Dir="BitDir.Rtl" Href="https://github.com/bitfoundation/bitplatform">پیوند راست به چپ</BitLink> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.cs index 5e3a9ce660..46d5227978 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.cs @@ -35,6 +35,15 @@ public partial class BitLinkDemo Description = "Callback for when the link clicked.", }, new() + { + Name = "Rel", + Type = "BitAnchorRel?", + DefaultValue = "null", + Description = "If Href provided, specifies the relationship between the current document and the linked document.", + LinkType = LinkType.Link, + Href = "#link-rel", + }, + new() { Name = "Target", Type = "string?", @@ -94,6 +103,97 @@ public partial class BitLinkDemo } ]; + private readonly List<ComponentSubEnum> componentSubEnums = + [ + new() + { + Id = "link-rel", + Name = "BitAnchorRel", + Description = "", + Items = + [ + new() + { + Name = "Alternate", + Value = "1", + Description = "Provides a link to an alternate representation of the document. (i.e. print page, translated or mirror)" + }, + new() + { + Name = "Author", + Value = "2", + Description = "Provides a link to the author of the document." + }, + new() + { + Name = "Bookmark", + Value = "4", + Description = "Permanent URL used for bookmarking." + }, + new() + { + Name = "External", + Value = "8", + Description = "Indicates that the referenced document is not part of the same site as the current document." + }, + new() + { + Name = "Help", + Value = "16", + Description = "Provides a link to a help document." + }, + new() + { + Name = "License", + Value = "32", + Description = "Provides a link to licensing information for the document." + }, + new() + { + Name = "Next", + Value = "64", + Description = "Provides a link to the next document in the series." + }, + new() + { + Name = "NoFollow", + Value = "128", + Description = @"Links to an unendorsed document, like a paid link. (""NoFollow"" is used by Google, to specify that the Google search spider should not follow that link)" + }, + new() + { + Name = "NoOpener", + Value = "256", + Description = "Requires that any browsing context created by following the hyperlink must not have an opener browsing context." + }, + new() + { + Name = "NoReferrer", + Value = "512", + Description = "Makes the referrer unknown. No referrer header will be included when the user clicks the hyperlink." + }, + new() + { + Name = "Prev", + Value = "1024", + Description = "The previous document in a selection." + }, + new() + { + Name = "Search", + Value = "2048", + Description = "Links to a search tool for the document." + }, + new() + { + Name = "Tag", + Value = "4096", + Description = "A tag (keyword) for the current document." + } + ] + } + ]; + private void HandleOnClick() @@ -177,6 +277,10 @@ eleifend dictum massa. Fusce eu sapien sit amet odio lacinia placerat. Mauris va "; private readonly string example7RazorCode = @" +<BitLink Rel=""BitAnchorRel.NoFollow"" Href=""https://github.com/bitfoundation/bitplatform"">Link with a rel attribute (nofollow)</BitLink> +<BitLink Rel=""BitAnchorRel.NoFollow | BitAnchorRel.NoReferrer"" Href=""https://github.com/bitfoundation/bitplatform"">Link with a rel attribute (nofollow & noreferrer)</BitLink>"; + + private readonly string example8RazorCode = @" <style> .custom-class { padding: 0.5rem; @@ -188,6 +292,6 @@ eleifend dictum massa. Fusce eu sapien sit amet odio lacinia placerat. Mauris va <BitLink Style=""color: goldenrod; font-weight:bold"" Href=""https://github.com/bitfoundation/bitplatform"">Link with style</BitLink> <BitLink Class=""custom-class"" Href=""https://github.com/bitfoundation/bitplatform"">Link with class</BitLink>"; - private readonly string example8RazorCode = @" + private readonly string example9RazorCode = @" <BitLink Dir=""BitDir.Rtl"" Href=""https://github.com/bitfoundation/bitplatform"">پیوند راست به چپ</BitLink>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor index e5b68cb450..d9fdcacb56 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor @@ -7,7 +7,7 @@ <ComponentDemo ComponentName="Overlay" ComponentDescription="The Overlay component is used to provide emphasis on a particular element or parts of it. It signals to the user of a state change within the application and can be used for creating loaders, dialogs and more." ComponentParameters="componentParameters"> - <ComponentExampleBox Title="Basic Overlay" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> + <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1"> <ExamplePreview> <div> <BitButton OnClick="() => BasicIsOpen = true">Show Overlay</BitButton> @@ -20,6 +20,8 @@ <ComponentExampleBox Title="Disabled AutoClose" RazorCode="@example2RazorCode" CsharpCode="@example2CsharpCode" Id="example2"> <ExamplePreview> + <div>Use BitOverlay with NoAutoClose to disable automatic closing when clicking outside the overlay.</div> + <br /> <div> <BitButton OnClick="() => AutoCloseIsOpen = true">Show Overlay</BitButton> <BitOverlay @bind-IsOpen="AutoCloseIsOpen" Class="overlay" NoAutoClose> @@ -40,7 +42,8 @@ <ComponentExampleBox Title="Absolute Positioning" RazorCode="@example3RazorCode" CsharpCode="@example3CsharpCode" Id="example3"> <ExamplePreview> - <div class="example-desc">Overlay can be positioned abosolutely inside a conainter with a relative position.</div> + <div>Overlay can be positioned abosolutely inside a conainter with a relative position.</div> + <br /> <div class="container"> <BitButton Class="show-button" OnClick="() => AbsoluteIsOpen = true">Show Overlay</BitButton> <BitOverlay @bind-IsOpen="AbsoluteIsOpen" @@ -55,15 +58,16 @@ <ComponentExampleBox Title="AutoToggleScroll" RazorCode="@example4RazorCode" CsharpCode="@example4CsharpCode" Id="example4"> <ExamplePreview> - <div class="example-desc">Scrolling cannot be left enabled behind the Overlay</div> + <div>Scrolling cannot be left enabled behind the Overlay</div> + <br /> <div> <BitButton OnClick="() => AutoToggleIsOpen = true">Show Overlay</BitButton> <BitOverlay @bind-IsOpen="AutoToggleIsOpen" Class="overlay" AutoToggleScroll> - <BitStack HorizontalAlign="BitAlignment.Stretch"> + <BitStack Alignment="BitAlignment.Center"> <BitText Style="color: dodgerblue;" Typography="BitTypography.H3">Please wait...</BitText> - <BitProgress Indeterminate Thickness="10"/> + <BitProgress Indeterminate Thickness="10" Style="width: 19rem;"/> </BitStack> </BitOverlay> </div> @@ -72,8 +76,8 @@ <ComponentExampleBox Title="Scroller Selector" RazorCode="@example5RazorCode" CsharpCode="@example5CsharpCode" Id="example5"> <ExamplePreview> - <div class="example-desc">Set specific scroller element to toggle the scrollbar.</div> - + <div>Set specific scroller element to toggle the scrollbar.</div> + <br /> <div class="btn-container"> <BitButton OnClick="() => EnabledScrollerIsOpen = true">Show with Enabled scrolling</BitButton> @@ -143,6 +147,8 @@ <ComponentExampleBox Title="Events" RazorCode="@example6RazorCode" CsharpCode="@example6CsharpCode" Id="example6"> <ExamplePreview> + <div>Use BitOverlay with event handlers to handle custom actions on user interactions, such as clicking outside to close.</div> + <br /> <div> <BitButton OnClick="() => EventOnCloseIsOpen = true">Show Overlay</BitButton> <BitOverlay @bind-IsOpen="EventOnCloseIsOpen" Class="overlay" OnClick=@(() => EventOnCloseIsOpen = false) NoAutoClose> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor.cs index d9148de57d..d533b06ec4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Overlay/BitOverlayDemo.razor.cs @@ -160,8 +160,7 @@ turpis. In hac habitasse platea dictumst. <h3>This is Container</h3>"; private readonly string example3CsharpCode = @" -private bool AbsoluteIsOpen; -"; +private bool AbsoluteIsOpen;"; private readonly string example4RazorCode = @" <style> @@ -177,9 +176,9 @@ turpis. In hac habitasse platea dictumst. <BitButton OnClick=""() => AutoToggleIsOpen = true"">Show Overlay</BitButton> <BitOverlay @bind-IsOpen=""AutoToggleIsOpen"" Class=""overlay"" AutoToggleScroll> - <BitStack HorizontalAlign=""BitAlignment.Stretch""> + <BitStack Alignment=""BitAlignment.Center""> <BitText Style=""color: dodgerblue;"" Typography=""BitTypography.H3"">Please wait...</BitText> - <BitProgress Indeterminate Thickness=""10""/> + <BitProgress Indeterminate Thickness=""10"" Style=""width: 19rem;""/> </BitStack> </BitOverlay>"; private readonly string example4CsharpCode = @" @@ -285,8 +284,6 @@ mollis. Curabitur ultricies leo ac metus venenatis elementum. private bool EnabledScrollerIsOpen; private bool DisabledScrollerIsOpen;"; - - private readonly string example6RazorCode = @" <style> .overlay { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor index 2f2b3bbe47..464ac358ec 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor @@ -23,6 +23,7 @@ </div> </ExamplePreview> </ComponentExampleBox> + <ComponentExampleBox Title="Vertical" RazorCode="@example2RazorCode" Id="example2"> <ExamplePreview> <div class="custom-horizontal-layout"> @@ -38,6 +39,7 @@ </div> </ExamplePreview> </ComponentExampleBox> + <ComponentExampleBox Title="AlignContent" RazorCode="@example3RazorCode" Id="example3"> <ExamplePreview> <div>Horizontal</div><br /> @@ -55,6 +57,7 @@ </div> </ExamplePreview> </ComponentExampleBox> + <ComponentExampleBox Title="FullWidth" RazorCode="@example4RazorCode" Id="example4"> <ExamplePreview> <div>While using inside a flex container with align-items css style:</div> @@ -66,5 +69,27 @@ </div> </ExamplePreview> </ComponentExampleBox> + + <ComponentExampleBox Title="Colors" RazorCode="@example5RazorCode" Id="example5"> + <ExamplePreview> + <div>Background:</div><br /> + <BitSeparator Background="BitColorKind.Primary">Primary</BitSeparator> + <br /> + <BitSeparator Background="BitColorKind.Secondary">Secondary</BitSeparator> + <br /> + <BitSeparator Background="BitColorKind.Tertiary">Tertiary</BitSeparator> + <br /> + <BitSeparator Background="BitColorKind.Transparent">Transparent</BitSeparator> + <br /><br /><br /> + <div>Border:</div><br /> + <BitSeparator Border="BitColorKind.Primary">Primary</BitSeparator> + <br /> + <BitSeparator Border="BitColorKind.Secondary">Secondary</BitSeparator> + <br /> + <BitSeparator Border="BitColorKind.Tertiary">Tertiary</BitSeparator> + <br /> + <BitSeparator Border="BitColorKind.Transparent">Transparent</BitSeparator> + </ExamplePreview> + </ComponentExampleBox> </ComponentDemo> </div> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor.cs index e0f7a1e7ee..09a472e512 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Separator/BitSeparatorDemo.razor.cs @@ -112,6 +112,17 @@ public partial class BitSeparatorDemo <BitSeparator>Default</BitSeparator> <BitSeparator AutoSize>AutoSize</BitSeparator> </div>"; + + private readonly string example5RazorCode = @" +<BitSeparator Background=""BitColorKind.Primary"">Primary</BitSeparator> +<BitSeparator Background=""BitColorKind.Secondary"">Secondary</BitSeparator> +<BitSeparator Background=""BitColorKind.Tertiary"">Tertiary</BitSeparator> +<BitSeparator Background=""BitColorKind.Transparent"">Transparent</BitSeparator> + +<BitSeparator Border=""BitColorKind.Primary"">Primary</BitSeparator> +<BitSeparator Border=""BitColorKind.Secondary"">Secondary</BitSeparator> +<BitSeparator Border=""BitColorKind.Tertiary"">Tertiary</BitSeparator> +<BitSeparator Border=""BitColorKind.Transparent"">Transparent</BitSeparator>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor index 1ed655a0a2..05ecee591e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor @@ -11,27 +11,99 @@ ComponentSubEnums="componentSubEnums"> <ComponentExampleBox Title="Basic" RazorCode="@example1RazorCode" Id="example1"> <ExamplePreview> - <div class="example-box"> - <BitText>This is default (Subtitle1)</BitText> - <br /> - <BitText Typography="BitTypography.H1">H1. Heading</BitText> - <BitText Typography="BitTypography.H2">H2. Heading</BitText> - <BitText Typography="BitTypography.H3">H3. Heading</BitText> - <BitText Typography="BitTypography.H4">H4. Heading</BitText> - <BitText Typography="BitTypography.H5">H5. Heading</BitText> - <BitText Typography="BitTypography.H6">H6. Heading</BitText> + <BitText>This is default (Subtitle1)</BitText> + <br /><br /> + <BitText Typography="BitTypography.H1">H1. Heading</BitText> + <br /> + <BitText Typography="BitTypography.H2">H2. Heading</BitText> + <br /> + <BitText Typography="BitTypography.H3">H3. Heading</BitText> + <br /> + <BitText Typography="BitTypography.H4">H4. Heading</BitText> + <br /> + <BitText Typography="BitTypography.H5">H5. Heading</BitText> + <br /> + <BitText Typography="BitTypography.H6">H6. Heading</BitText> + <br /><br /> + <BitText Typography="BitTypography.Subtitle1">Subtitle1. Lorem ipsum dolor sit amet</BitText> + <br /> + <BitText Typography="BitTypography.Subtitle2">Subtitle2. Lorem ipsum dolor sit amet</BitText> + <br /><br /> + <BitText Typography="BitTypography.Body1">Body1. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam.</BitText> + <br /> + <BitText Typography="BitTypography.Body2">Body2. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam.</BitText> + <br /><br /> + <BitText Typography="BitTypography.Button">Button. Click Me</BitText> + <br /> + <BitText Typography="BitTypography.Caption1">Caption1. Hello World!</BitText> + <br /> + <BitText Typography="BitTypography.Caption2">Caption2. Hello World!</BitText> + <br /> + <BitText Typography="BitTypography.Overline">Overline. this is overline text.</BitText> + </ExamplePreview> + </ComponentExampleBox> - <BitText Typography="BitTypography.Subtitle1">Subtitle1. Lorem ipsum dolor sit amet</BitText> - <BitText Typography="BitTypography.Subtitle2">Subtitle2. Lorem ipsum dolor sit amet</BitText> + <ComponentExampleBox Title="Align" RazorCode="@example2RazorCode" Id="example2"> + <ExamplePreview> + <BitText Align="BitTextAlign.Start">Start</BitText> + <br /> + <BitText Align="BitTextAlign.Center">Center</BitText> + <br /> + <BitText Align="BitTextAlign.End">End</BitText> + </ExamplePreview> + </ComponentExampleBox> - <BitText Typography="BitTypography.Body1">Body1. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam.</BitText> - <BitText Typography="BitTypography.Body2">Body2. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam.</BitText> + <ComponentExampleBox Title="Foreground" RazorCode="@example3RazorCode" Id="example3"> + <ExamplePreview> + <BitText Foreground="BitColorKind.Primary">Primary foreground</BitText> + <br /> + <BitText Foreground="BitColorKind.Secondary">Secondary foreground</BitText> + <br /> + <BitText Foreground="BitColorKind.Tertiary">Tertiary foreground</BitText> + <br /> + <div style="background:linear-gradient(blue, pink);background-clip:text;"> + <BitText Foreground="BitColorKind.Transparent">Transparent foreground</BitText> + </div> + </ExamplePreview> + </ComponentExampleBox> - <BitText Typography="BitTypography.Button">Button. Click Me</BitText> - <BitText Typography="BitTypography.Caption1">Caption1. Hello World!</BitText> - <BitText Typography="BitTypography.Caption2">Caption2. Hello World!</BitText> - <BitText Typography="BitTypography.Overline">Overline. this is overline text.</BitText> + <ComponentExampleBox Title="Color" RazorCode="@example4RazorCode" Id="example4"> + <ExamplePreview> + <BitText Color="BitColor.Primary">Primary color</BitText> + <br /> + <BitText Color="BitColor.Secondary">Secondary color</BitText> + <br /> + <BitText Color="BitColor.Tertiary">Tertiary color</BitText> + <br /> + <BitText Color="BitColor.Info">Info color</BitText> + <br /> + <BitText Color="BitColor.Success">Success color</BitText> + <br /> + <BitText Color="BitColor.Warning">Warning color</BitText> + <br /> + <BitText Color="BitColor.SevereWarning">SevereWarning color</BitText> + <br /> + <BitText Color="BitColor.Error">Error color</BitText> + <br /> + <div style="background:var(--bit-clr-fg-sec);padding:1rem"> + <BitText Color="BitColor.PrimaryBackground">PrimaryBackground color</BitText> + <br /> + <BitText Color="BitColor.SecondaryBackground">SecondaryBackground color</BitText> + <br /> + <BitText Color="BitColor.TertiaryBackground">TertiaryBackground color</BitText> </div> + <br /> + <BitText Color="BitColor.PrimaryForeground">PrimaryForeground color</BitText> + <br /> + <BitText Color="BitColor.SecondaryForeground">SecondaryForeground color</BitText> + <br /> + <BitText Color="BitColor.TertiaryForeground">TertiaryForeground color</BitText> + <br /> + <BitText Color="BitColor.PrimaryBorder">PrimaryBorder color</BitText> + <br /> + <BitText Color="BitColor.SecondaryBorder">SecondaryBorder color</BitText> + <br /> + <BitText Color="BitColor.TertiaryBorder">TertiaryBorder color</BitText> </ExamplePreview> </ComponentExampleBox> </ComponentDemo> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.cs index 1b5eb4160e..5fb74aba4c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.cs @@ -12,6 +12,16 @@ public partial class BitTextDemo Description = "The content of the text.", }, new() + { + Name = "Color", + Type = "BitColor?", + DefaultValue = "null", + Description = "The general color of the text.", + LinkType = LinkType.Link, + Href = "#color-enum" + + }, + new() { Name = "Element", Type = "string?", @@ -20,10 +30,12 @@ public partial class BitTextDemo }, new() { - Name = "NoWrap", - Type = "bool", - DefaultValue = "false", - Description = "If true, the text will not wrap, but instead will truncate with a text overflow ellipsis.", + Name = "Foreground", + Type = "BitColorKind?", + DefaultValue = "null", + Description = "The kind of the foreground color of the text.", + LinkType = LinkType.Link, + Href = "#color-kind-enum" }, new() { @@ -33,6 +45,13 @@ public partial class BitTextDemo Description = "If true, the text will have a bottom margin.", }, new() + { + Name = "NoWrap", + Type = "bool", + DefaultValue = "false", + Description = "If true, the text will not wrap, but instead will truncate with a text overflow ellipsis.", + }, + new() { Name = "Typography", Type = "BitTypography?", @@ -45,6 +64,150 @@ public partial class BitTextDemo private readonly List<ComponentSubEnum> componentSubEnums = [ + new() + { + Id = "color-enum", + Name = "BitColor", + Description = "Defines the general colors available in the bit BlazorUI.", + Items = + [ + new() + { + Name= "Primary", + Description="Info Primary general color.", + Value="0", + }, + new() + { + Name= "Secondary", + Description="Secondary general color.", + Value="1", + }, + new() + { + Name= "Tertiary", + Description="Tertiary general color.", + Value="2", + }, + new() + { + Name= "Info", + Description="Info general color.", + Value="3", + }, + new() + { + Name= "Success", + Description="Success general color.", + Value="4", + }, + new() + { + Name= "Warning", + Description="Warning general color.", + Value="5", + }, + new() + { + Name= "SevereWarning", + Description="SevereWarning general color.", + Value="6", + }, + new() + { + Name= "Error", + Description="Error general color.", + Value="7", + }, + new() + { + Name= "PrimaryBackground", + Description="Primary background color.", + Value="8", + }, + new() + { + Name= "SecondaryBackground", + Description="Secondary background color.", + Value="9", + }, + new() + { + Name= "TertiaryBackground", + Description="Tertiary background color.", + Value="10", + }, + new() + { + Name= "PrimaryForeground", + Description="Primary foreground color.", + Value="11", + }, + new() + { + Name= "SecondaryForeground", + Description="Secondary foreground color.", + Value="12", + }, + new() + { + Name= "TertiaryForeground", + Description="Tertiary foreground color.", + Value="13", + }, + new() + { + Name= "PrimaryBorder", + Description="Primary border color.", + Value="14", + }, + new() + { + Name= "SecondaryBorder", + Description="Secondary border color.", + Value="15", + }, + new() + { + Name= "TertiaryBorder", + Description="Tertiary border color.", + Value="16", + } + ] + }, + new() + { + Id = "color-kind-enum", + Name = "BitColorKind", + Description = "Defines the color kinds available in the bit BlazorUI.", + Items = + [ + new() + { + Name = "Primary", + Description = "The primary color kind.", + Value = "0", + }, + new() + { + Name = "Secondary", + Description = "The secondary color kind.", + Value = "1", + }, + new() + { + Name = "Tertiary", + Description = "The tertiary color kind.", + Value = "2", + }, + new() + { + Name = "Transparent", + Description = "The transparent color kind.", + Value = "3", + }, + ] + }, new() { Id = "typography-enum", @@ -92,4 +255,41 @@ public partial class BitTextDemo <BitText Typography=""BitTypography.Caption1"">Caption1. Hello World!</BitText> <BitText Typography=""BitTypography.Caption2"">Caption2. Hello World!</BitText> <BitText Typography=""BitTypography.Overline"">Overline. this is overline text.</BitText>"; + + private string example2RazorCode = @" +<BitText Align=""BitTextAlign.Start"">Start</BitText> +<BitText Align=""BitTextAlign.Center"">Center</BitText> +<BitText Align=""BitTextAlign.End"">End</BitText>"; + + private string example3RazorCode = @" +<BitText Foreground=""BitColorKind.Primary"">Primary foreground</BitText> +<BitText Foreground=""BitColorKind.Secondary"">Secondary foreground</BitText> +<BitText Foreground=""BitColorKind.Tertiary"">Tertiary foreground</BitText> + +<div style=""background:linear-gradient(blue, pink);background-clip:text;""> + <BitText Foreground=""BitColorKind.Transparent"">Transparent foreground</BitText> +</div>"; + + private string example4RazorCode = @" +<BitText Color=""BitColor.Primary"">Primary color</BitText> +<BitText Color=""BitColor.Secondary"">Secondary color</BitText> +<BitText Color=""BitColor.Tertiary"">Tertiary color</BitText> + +<BitText Color=""BitColor.Info"">Info color</BitText> +<BitText Color=""BitColor.Success"">Success color</BitText> +<BitText Color=""BitColor.Warning"">Warning color</BitText> +<BitText Color=""BitColor.SevereWarning"">SevereWarning color</BitText> +<BitText Color=""BitColor.Error"">Error color</BitText> + +<BitText Color=""BitColor.PrimaryBackground"">PrimaryBackground color</BitText> +<BitText Color=""BitColor.SecondaryBackground"">SecondaryBackground color</BitText> +<BitText Color=""BitColor.TertiaryBackground"">TertiaryBackground color</BitText> + +<BitText Color=""BitColor.PrimaryForeground"">PrimaryForeground color</BitText> +<BitText Color=""BitColor.SecondaryForeground"">SecondaryForeground color</BitText> +<BitText Color=""BitColor.TertiaryForeground"">TertiaryForeground color</BitText> + +<BitText Color=""BitColor.PrimaryBorder"">PrimaryBorder color</BitText> +<BitText Color=""BitColor.SecondaryBorder"">SecondaryBorder color</BitText> +<BitText Color=""BitColor.TertiaryBorder"">TertiaryBorder color</BitText>"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.scss index c0d02b2feb..e69de29bb2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Text/BitTextDemo.razor.scss @@ -1,8 +0,0 @@ -@import '../../../../Styles/abstracts/_functions.scss'; - -.example-box { - display: flex; - gap: rem2(15px); - width: fit-content; - flex-flow: column nowrap; -} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor index 3b2ac3085e..d01878f361 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor @@ -241,6 +241,9 @@ <BitLink Href="/components/chart" title="Chart" Style="display: flex"> <BitText Typography="BitTypography.Subtitle1">Chart</BitText> </BitLink> + <BitLink Href="/components/pdfreader" title="PdfReader" Style="display: flex"> + <BitText Typography="BitTypography.Subtitle1">PdfReader</BitText> + </BitLink> </div> </div> </div> \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor index 57500508d5..7da3d297a4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor @@ -15,7 +15,7 @@ <div class="component-box @BitCss.Class.Color.Background.Primary"> <BitDropdown Label="Select multiple products" Items="_productDropdownItems" - IsMultiSelect="true" + MultiSelect="true" Placeholder="Select an option" Style="width: 326px; margin: 20px 0 20px 0" TItem="BitDropdownItem<string>" TValue="string" /> @@ -62,10 +62,10 @@ </BitButton> </div> <BitProgress Thickness="20" Style="margin-bottom: 30px" Indeterminate /> - <BitPivot HeadersOnly - LinkSize="@BitSize.Large" + <BitPivot HeaderOnly + Size="@BitSize.Large" DefaultSelectedKey="Overview" - LinkFormat="@BitPivotLinkFormat.Links" + HeaderType="@BitPivotHeaderType.Link" OnItemClick="@(item => _pivotSelectedKey = item.Key)"> <BitPivotItem HeaderText="Overview" Key="Overview"></BitPivotItem> <BitPivotItem HeaderText="Features" Key="Features"></BitPivotItem> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor.scss index b008d11fe2..ee05e66c7f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/HeroSection.razor.scss @@ -62,10 +62,11 @@ } .component-box--transparent { + gap: 1rem; display: flex; - justify-content: flex-start; width: rem2(360px); margin-bottom: rem2(40px); + justify-content: flex-start; } .tab-content { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor index 9d05ccf145..f24774cd76 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor @@ -5,7 +5,7 @@ <div class="content-container"> <BitText Typography="BitTypography.H3" Gutter>Popular components</BitText> <div class="component-list"> - @foreach (var com in Components) + @foreach (var com in _components) { <button @onclick="(() => SelectComponent(com))" class="component-row @(com.Name == SelectedComponent?.Name ? "component-row--active" : null) @BitCss.Class.Color.Foreground.Primary"> @@ -23,8 +23,8 @@ </button> } </div> - <BitCarousel Class="component-carousel" ShowNextPrev="false" OnChange="index => SelectComponent(Components[index])"> - @foreach (var com in Components) + <BitCarousel Class="component-carousel" HideNextPrev OnChange="index => SelectComponent(_components[index])"> + @foreach (var com in _components) { <BitCarouselItem Style="width: 100%"> <button class="component-row @(com.Name == SelectedComponent?.Name ? "component-row--active" : null)"> @@ -59,22 +59,23 @@ <br /> @if (SelectedComponent?.Name == "ColorPicker") { - <BitColorPicker ShowAlphaSlider="true" ShowPreview="true" @bind-Alpha="Alpha" @bind-Color="@Color" /> + <BitColorPicker ShowAlphaSlider ShowPreview @bind-Alpha="Alpha" @bind-Color="@Color" /> <br /> <div>Alpha: @Alpha</div> <div>Color: @Color</div> } else if (SelectedComponent?.Name == "DatePicker") { - <BitDatePicker @bind-Value="SelectedDate" Placeholder="Select a date" Style="width: 300px" ShowWeekNumbers="true" /> + <BitDatePicker @bind-Value="SelectedDate" Placeholder="Select a date" Style="width: 300px" ShowWeekNumbers /> <br /> <div>Selected Date: <b>@SelectedDate?.ToString("dd MMM yyyy")</b></div> } else if (SelectedComponent?.Name == "FileUpload") { - <BitFileUpload IsMultiSelect="true" + <BitFileUpload AutoUpload + MultiSelect + ChunkedUpload Label="Select files" - AutoUploadEnabled="true" UploadUrl="@UploadUrl" RemoveUrl="@RemoveUrl" MaxSize="1024 * 1024 * 500" /> @@ -82,10 +83,10 @@ else if (SelectedComponent?.Name == "Dropdown") { <BitDropdown Label="Multi-select Dropdown" + MultiSelect Items="DropdownItems" @bind-Values="SelectedDropdownValues" Placeholder="Select options" - IsMultiSelect="true" Style="width:290px" /> <br /> <div>Selected Items: @string.Join(", ", SelectedDropdownValues)</div> @@ -115,8 +116,7 @@ @if (SelectedComponent?.Name == "ColorPicker") { <pre class="code"> - <code class="language-cshtml"><BitColorPicker ShowPreview="true" - ShowAlphaSlider="true" + <code class="language-cshtml"><BitColorPicker ShowPreview ShowAlphaSlider @@bind-Alpha="Alpha" @@bind-Color="@@Color" /> <div>Alpha: @@Alpha</div> @@ -133,7 +133,7 @@ <code class="language-cshtml"><BitDatePicker @@bind-Value="SelectedDate" Placeholder="Select a date" Style="width: 300px" - ShowWeekNumbers="true" /> + ShowWeekNumbers /> <div>Selected Date: <b>@@SelectedDate?.ToString("dd MMM yyyy")</b></div> </code><br /><code class="language-csharp"> @@code { @@ -143,30 +143,32 @@ else if (SelectedComponent?.Name == "FileUpload") { <pre class="code"> - <code class="language-cshtml"><BitFileUpload Label="Select files" - IsMultiSelect="true" + <code class="language-cshtml"><BitFileUpload AutoUpload + MultiSelect + ChunkedUpload + Label="Select files" UploadUrl="@@UploadUrl" RemoveUrl="@@RemoveUrl" - Style="flex-grow:1; max-width:none;" /> + MaxSize="1024 * 1024 * 500" /> </code><br/><code class="language-csharp"> @@code { - private string UploadUrl = "FileUpload/UploadFile"; - private string RemoveUrl = "FileUpload/RemoveFile"; + private string UploadUrl = "/UploadFile"; + private string RemoveUrl = "/RemoveFile"; }</code></pre> } else if (SelectedComponent?.Name == "Dropdown") { <pre class="code"> <code class="language-cshtml"><BitDropdown Label="Multi-select Dropdown" + IsMultiSelect Items="DropdownItems" @@bind-Values="SelectedDropdownValues" Placeholder="Select options" - IsMultiSelect="true" Style="width:290px" /> <div>Selected Items: @@string.Join(", ", SelectedDropdownValues)</div> </code><br /><code class="language-csharp"> @@code { - private List<string> SelectedDropdownValues = new() { "f-app", "f-ban" }; + private IEnumerable<string> SelectedDropdownValues = new() { "f-app", "f-ban" }; private static List<BitDropdownItem> DropdownItems = new() { new() { ItemType = BitDropdownItemType.Header, Text = "Fruits" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor.cs index 2da81a40ad..64ff56109c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/PopularComponents.razor.cs @@ -5,8 +5,8 @@ public partial class PopularComponents private bool showCode; private DateTimeOffset? SelectedDate; - private List<PopularComponent> Components = new() - { + private List<PopularComponent> _components = + [ new() { Name = "ColorPicker", @@ -37,13 +37,13 @@ public partial class PopularComponents Description = "The Nav (TreeList) component provides links to the main areas of an app or site.", Url = "/components/nav" } - }; + ]; private PopularComponent? SelectedComponent; protected override async Task OnInitAsync() { - SelectedComponent = Components[0]; + SelectedComponent = _components[0]; await base.OnInitAsync(); } @@ -68,9 +68,9 @@ private void SelectComponent(PopularComponent com) - private ICollection<string?> SelectedDropdownValues = new List<string?>() { "f-app", "f-ban" }; - private static List<BitDropdownItem<string>> DropdownItems = new() - { + private IEnumerable<string?> SelectedDropdownValues = ["f-app", "f-ban"]; + private static List<BitDropdownItem<string>> DropdownItems = + [ new() { ItemType = BitDropdownItemType.Header, Text = "Fruits" }, new() { Text = "Apple", Value = "f-app" }, new() { Text = "Orange", Value = "f-ora", IsEnabled = false }, @@ -78,29 +78,29 @@ private void SelectComponent(PopularComponent com) new() { ItemType = BitDropdownItemType.Divider }, new() { ItemType = BitDropdownItemType.Header, Text = "Vegetables" }, new() { Text = "Broccoli", Value = "v-bro" } - }; + ]; private BitNavItem SelectedNavItem = NavItems[2]; - private static List<BitNavItem> NavItems = new() - { + private static List<BitNavItem> NavItems = + [ new() { Text = "Home", ExpandAriaLabel = "Expand Home section", CollapseAriaLabel = "Collapse Home section", IsExpanded = true, - ChildItems = new() - { + ChildItems = + [ new() { Text = "Activity" }, new() { Text = "MSN", IsEnabled = false } - } + ] }, new() { Text = "Documents", IsExpanded = true }, new() { Text = "Pages" }, new() { Text = "Notebook", IsEnabled = false }, new() { Text = "Communication and Media" }, new() { Text = "News", Title = "News", IconName = BitIconName.News }, - }; + ]; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Services/AppRenderMode.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Services/AppRenderMode.cs index a71b0742dc..32775f99a4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Services/AppRenderMode.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Services/AppRenderMode.cs @@ -13,7 +13,7 @@ public static class AppRenderMode public static IComponentRenderMode NoPrerenderBlazorWebAssembly => new InteractiveWebAssemblyRenderMode(prerender: false); public static IComponentRenderMode Current => - BuildConfiguration.IsDebug() ? BlazorServer /*For better development experience*/ : Auto; + BuildConfiguration.IsDebug() ? BlazorServer /*For better development experience*/ : BlazorWebAssembly; public static bool PwaEnabled { get; } = #if PwaEnabled diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/NavMenu.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/NavMenu.razor.cs index 1d987cde62..0a20b9d210 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/NavMenu.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/NavMenu.razor.cs @@ -151,7 +151,8 @@ public partial class NavMenu : IDisposable ChildItems = [ new() { Text = "DataGrid", Url = "/components/datagrid", AdditionalUrls = ["/components/data-grid"] }, - new() { Text = "Chart", Url = "/components/chart" } + new() { Text = "Chart", Url = "/components/chart" }, + new() { Text = "PdfReader", Url = "/components/pdfreader" }, ] }, new() { Text = "Iconography", Url = "/iconography" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json index cc62fc31b2..77b35e2ab3 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/article.pdf b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/article.pdf new file mode 100644 index 0000000000..65570184ac Binary files /dev/null and b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/article.pdf differ diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/hello-world.pdf b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/hello-world.pdf new file mode 100644 index 0000000000..986ee1e2f2 Binary files /dev/null and b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/wwwroot/samples/hello-world.pdf differ diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj index aba959983d..3cd9489d90 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj @@ -84,12 +84,12 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.5" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.6" /> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> @@ -102,10 +102,8 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.82" /> - <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.82" /> - <PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.82" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.82" /> + <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.93" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.93" /> </ItemGroup> <!-- Build Properties must be defined within these property groups to ensure successful publishing diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Platforms/Android/MainActivity.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Platforms/Android/MainActivity.cs index 4370f26922..5f00e79f9a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Platforms/Android/MainActivity.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Platforms/Android/MainActivity.cs @@ -3,7 +3,7 @@ namespace Bit.BlazorUI.Demo.Client.Maui.Platforms.Android; -[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, +[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTask, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/wwwroot/index.html b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/wwwroot/index.html index 89ed793d6a..0076c2e05e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/wwwroot/index.html +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/wwwroot/index.html @@ -23,14 +23,15 @@ <link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <link rel="stylesheet" href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" /> <link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/styles/app.css" /> - <link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.bundle.scp.css" /> + <link rel="stylesheet" href="Bit.BlazorUI.Demo.Client.Windows.styles.css" /> + <link rel="stylesheet" href="Bit.BlazorUI.Demo.Client.Maui.styles.css" /> <link rel="stylesheet" href="_content/Bit.BlazorUI.Demo.Client.Core/prism-1.28.0/prism-okaidia-bit.css" /> <style> body { background-color: #0D2960; } - + body.bit-windows, body.bit-macos, body.bit-ios { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj index b55418f627..2279b55d0a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj @@ -26,13 +26,13 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Bit.Bswup" Version="8.11.0" /> + <PackageReference Include="Bit.Bswup" Version="8.12.0" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js index 7e3299dcd2..ed897d21e5 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js @@ -1,19 +1,21 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; self.assetsExclude = [ /bit\.blazorui\.fluent\.css$/, /bit\.blazorui\.fluent-dark\.css$/, - /bit\.blazorui\.fluent-light\.css$/, - /Bit.BlazorUI.Demo\.Client\.Web\.styles\.css$/ + /bit\.blazorui\.fluent-light\.css$/ ]; self.externalAssets = [ { "url": "/" }, { - "url": "_framework\/blazor.web.js?v=8.0.8" + "url": "_framework\/blazor.web.js" + }, + { + "url": "Bit.BlazorUI.Demo.Server.styles.css" }, { "url": "https://www.googletagmanager.com/gtag/js?id=G-G1ET5L69QF" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/.config/dotnet-tools.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/.config/dotnet-tools.json index 6031cf8fe5..f8cf15add4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/.config/dotnet-tools.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "vpk": { - "version": "0.0.594", + "version": "0.0.869", "commands": [ "vpk" ] diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj index eb180201ea..26ca222543 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk.Razor"> +<Project Sdk="Microsoft.NET.Sdk.Razor"> <PropertyGroup> <OutputType>WinExe</OutputType> @@ -17,17 +17,17 @@ <Using Include="Bit.BlazorUI.Demo.Client.Core.Services.Contracts" /> <Using Include="Bit.BlazorUI.Demo.Client.Core.Services" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="8.0.82" /> - <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2739.15" /> - <PackageReference Include="Velopack" Version="0.0.594" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="8.0.93" /> + <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2849.39" /> + <PackageReference Include="Velopack" Version="0.0.869" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <Content Include="..\Bit.BlazorUI.Demo.Client.Maui\wwwroot\index.html" Link="wwwroot\index.html"> diff --git a/src/BlazorUI/Demo/Directory.Build.props b/src/BlazorUI/Demo/Directory.Build.props index 21d963b99f..6a45dd8049 100644 --- a/src/BlazorUI/Demo/Directory.Build.props +++ b/src/BlazorUI/Demo/Directory.Build.props @@ -1,4 +1,4 @@ -<!-- Generated by bit-bp template v-8.11.0 --> +<!-- Generated by bit-bp template v-8.12.0 --> <Project> <PropertyGroup> <LangVersion>12.0</LangVersion> diff --git a/src/Bswup/Bit.Bswup.Demo/Bit.Bswup.Demo.csproj b/src/Bswup/Bit.Bswup.Demo/Bit.Bswup.Demo.csproj index bd500fbff3..155b50b3aa 100644 --- a/src/Bswup/Bit.Bswup.Demo/Bit.Bswup.Demo.csproj +++ b/src/Bswup/Bit.Bswup.Demo/Bit.Bswup.Demo.csproj @@ -10,8 +10,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" /> </ItemGroup> <ItemGroup> diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js index 4e071fc51e..957cb2df33 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js index 428bc2ba8c..251ce54633 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/Bit.Bswup.NewDemo.Client.csproj b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/Bit.Bswup.NewDemo.Client.csproj index 32c0fec0a3..52369f2674 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/Bit.Bswup.NewDemo.Client.csproj +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/Bit.Bswup.NewDemo.Client.csproj @@ -13,7 +13,7 @@ <ItemGroup> <ProjectReference Include="..\..\Bit.Bswup\Bit.Bswup.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js index 0873587202..030e81c699 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js index f42bc9a032..9c7171472a 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 self.assetsInclude = []; self.assetsExclude = [ diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.csproj b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.csproj index 5a7a991857..b4e9760dd5 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.csproj +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.csproj @@ -8,7 +8,7 @@ <ItemGroup> <ProjectReference Include="..\Bit.Bswup.NewDemo.Client\Bit.Bswup.NewDemo.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> </ItemGroup> </Project> diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts index 54eb0e8241..058e2b19f4 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts @@ -1,4 +1,4 @@ -window['bit-bswup.progress version'] = '8.11.0'; +window['bit-bswup.progress version'] = '8.12.0'; ; (function () { (window as any).startBswupProgress = (autoReload: boolean, diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts index 678a56b48c..09d59e12bc 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts @@ -1,4 +1,4 @@ -self['bit-bswup.sw version'] = '8.11.0'; +self['bit-bswup.sw version'] = '8.12.0'; interface Window { clients: any diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts index a52fa93234..6b84c914b9 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts @@ -1,5 +1,5 @@ const BitBswup = {} as any; -BitBswup.version = window['bit-bswup version'] = '8.11.0'; +BitBswup.version = window['bit-bswup version'] = '8.12.0'; declare const Blazor: any; diff --git a/src/Bswup/Bit.Bswup/package-lock.json b/src/Bswup/Bit.Bswup/package-lock.json index ec6374a581..37334b77c0 100644 --- a/src/Bswup/Bit.Bswup/package-lock.json +++ b/src/Bswup/Bit.Bswup/package-lock.json @@ -5,14 +5,14 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -27,9 +27,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -44,9 +44,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -61,9 +61,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -78,9 +78,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -95,9 +95,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -112,9 +112,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -129,9 +129,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -146,9 +146,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -163,9 +163,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -180,9 +180,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -197,9 +197,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -214,9 +214,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -231,9 +231,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -248,9 +248,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -265,9 +265,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -282,9 +282,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -299,9 +299,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -316,9 +316,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -350,9 +350,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -367,9 +367,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -384,9 +384,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -401,9 +401,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -431,36 +431,36 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Bswup/Bit.Bswup/package.json b/src/Bswup/Bit.Bswup/package.json index 90c59d8ad7..d1553de999 100644 --- a/src/Bswup/Bit.Bswup/package.json +++ b/src/Bswup/Bit.Bswup/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } } diff --git a/src/Bswup/FullDemo/Client/Bit.Bswup.Demo.Client.csproj b/src/Bswup/FullDemo/Client/Bit.Bswup.Demo.Client.csproj index f9c5734c40..d1f9904cf5 100644 --- a/src/Bswup/FullDemo/Client/Bit.Bswup.Demo.Client.csproj +++ b/src/Bswup/FullDemo/Client/Bit.Bswup.Demo.Client.csproj @@ -11,8 +11,8 @@ <ItemGroup> <ProjectReference Include="..\..\Bit.Bswup\Bit.Bswup.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> </ItemGroup> diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js index d123f3c81b..c829d59c62 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js index e9337d36af..5dff71b0e4 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 8.11.0 +// bit version: 8.12.0 self.assetsInclude = []; self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; diff --git a/src/Bswup/FullDemo/Server/Bit.Bswup.Demo.Server.csproj b/src/Bswup/FullDemo/Server/Bit.Bswup.Demo.Server.csproj index 9d20299ee5..029d302711 100644 --- a/src/Bswup/FullDemo/Server/Bit.Bswup.Demo.Server.csproj +++ b/src/Bswup/FullDemo/Server/Bit.Bswup.Demo.Server.csproj @@ -8,7 +8,7 @@ <ItemGroup> <ProjectReference Include="..\..\Bit.Bswup\Bit.Bswup.csproj" /> <ProjectReference Include="..\Client\Bit.Bswup.Demo.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> </ItemGroup> </Project> diff --git a/src/Bup/Bit.Bup.Demo/Bit.Bup.Demo.csproj b/src/Bup/Bit.Bup.Demo/Bit.Bup.Demo.csproj index 9d2b40b543..d594e34c78 100644 --- a/src/Bup/Bit.Bup.Demo/Bit.Bup.Demo.csproj +++ b/src/Bup/Bit.Bup.Demo/Bit.Bup.Demo.csproj @@ -9,8 +9,8 @@ <ItemGroup> <ProjectReference Include="..\Bit.Bup\Bit.Bup.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" /> </ItemGroup> <ItemGroup> diff --git a/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts b/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts index 4e0116c18b..b88ecc5f3b 100644 --- a/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts +++ b/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts @@ -1,4 +1,4 @@ -window['bit-bup.progress version'] = '8.11.0'; +window['bit-bup.progress version'] = '8.12.0'; ; (function () { (window as any).startBupProgress = (showLogs: boolean, showAssets: boolean, appContainerSelector: string, hideApp: boolean, autoHide: boolean) => { diff --git a/src/Bup/Bit.Bup/Scripts/bit-bup.ts b/src/Bup/Bit.Bup/Scripts/bit-bup.ts index d3014769dc..a3b3d90c47 100644 --- a/src/Bup/Bit.Bup/Scripts/bit-bup.ts +++ b/src/Bup/Bit.Bup/Scripts/bit-bup.ts @@ -1,5 +1,5 @@ var BitBup = BitBup || {}; -BitBup.version = window['bit-bup version'] = '8.11.0'; +BitBup.version = window['bit-bup version'] = '8.12.0'; declare const Blazor: any; diff --git a/src/Bup/Bit.Bup/package-lock.json b/src/Bup/Bit.Bup/package-lock.json index ed13e20130..d4470216c2 100644 --- a/src/Bup/Bit.Bup/package-lock.json +++ b/src/Bup/Bit.Bup/package-lock.json @@ -5,14 +5,14 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -27,9 +27,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -44,9 +44,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -61,9 +61,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -78,9 +78,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -95,9 +95,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -112,9 +112,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -129,9 +129,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -146,9 +146,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -163,9 +163,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -180,9 +180,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -197,9 +197,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -214,9 +214,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -231,9 +231,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -248,9 +248,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -265,9 +265,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -282,9 +282,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -299,9 +299,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -316,9 +316,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -350,9 +350,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -367,9 +367,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -384,9 +384,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -401,9 +401,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -431,36 +431,36 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Bup/Bit.Bup/package.json b/src/Bup/Bit.Bup/package.json index 90c59d8ad7..d1553de999 100644 --- a/src/Bup/Bit.Bup/package.json +++ b/src/Bup/Bit.Bup/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } } diff --git a/src/Bup/FullDemo/Client/Bit.Bup.Demo.Client.csproj b/src/Bup/FullDemo/Client/Bit.Bup.Demo.Client.csproj index 34e9ddad62..12f6640d83 100644 --- a/src/Bup/FullDemo/Client/Bit.Bup.Demo.Client.csproj +++ b/src/Bup/FullDemo/Client/Bit.Bup.Demo.Client.csproj @@ -9,7 +9,7 @@ <ItemGroup> <ProjectReference Include="..\..\Bit.Bup\Bit.Bup.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" /> </ItemGroup> </Project> diff --git a/src/Bup/FullDemo/Server/Bit.Bup.Demo.Server.csproj b/src/Bup/FullDemo/Server/Bit.Bup.Demo.Server.csproj index d471eca680..20ddd339d8 100644 --- a/src/Bup/FullDemo/Server/Bit.Bup.Demo.Server.csproj +++ b/src/Bup/FullDemo/Server/Bit.Bup.Demo.Server.csproj @@ -8,7 +8,7 @@ <ItemGroup> <ProjectReference Include="..\..\Bit.Bup\Bit.Bup.csproj" /> <ProjectReference Include="..\Client\Bit.Bup.Demo.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> </ItemGroup> </Project> diff --git a/src/Butil/Bit.Butil/BitButil.cs b/src/Butil/Bit.Butil/BitButil.cs index ecd4bbbc67..c810bf007b 100644 --- a/src/Butil/Bit.Butil/BitButil.cs +++ b/src/Butil/Bit.Butil/BitButil.cs @@ -6,21 +6,23 @@ public static class BitButil { public static IServiceCollection AddBitButilServices(this IServiceCollection services) { - services.AddTransient<Window>(); - services.AddTransient<Document>(); - services.AddTransient<Keyboard>(); + services.AddTransient<Clipboard>(); services.AddTransient<Console>(); + services.AddTransient<Cookie>(); + services.AddTransient<Crypto>(); + services.AddTransient<Document>(); services.AddTransient<History>(); - services.AddTransient<Navigator>(); + services.AddTransient<Keyboard>(); services.AddTransient<LocalStorage>(); services.AddTransient<SessionStorage>(); services.AddTransient<Location>(); + services.AddTransient<Navigator>(); + services.AddTransient<Notification>(); services.AddTransient<Screen>(); - services.AddTransient<Cookie>(); - services.AddTransient<Crypto>(); - services.AddTransient<Clipboard>(); - services.AddTransient<VisualViewport>(); services.AddTransient<ScreenOrientation>(); + services.AddTransient<UserAgent>(); + services.AddTransient<VisualViewport>(); + services.AddTransient<Window>(); return services; } diff --git a/src/Butil/Bit.Butil/Internals/Notification/InternalNotificationOptions.cs b/src/Butil/Bit.Butil/Internals/Notification/InternalNotificationOptions.cs new file mode 100644 index 0000000000..46b3ec7490 --- /dev/null +++ b/src/Butil/Bit.Butil/Internals/Notification/InternalNotificationOptions.cs @@ -0,0 +1,18 @@ +namespace Bit.Butil; + +internal class InternalNotificationOptions(NotificationOptions options) +{ + public string? badge { get; set; } = options.Badge; + public string? body { get; set; } = options.Body; + public object? data { get; set; } = options.Data; + public string? dir { get; set; } = options.Dir.ToString().ToLower(); + public string? icon { get; set; } = options.Icon; + public string? image { get; set; } = options.Image; + public string? lang { get; set; } = options.Lang; + public bool? renotify { get; set; } = options.Renotify; + public bool requireInteraction { get; set; } = options.RequireInteraction; + public bool? silent { get; set; } = options.Silent; + public string? tag { get; set; } = options.Tag; + public long? timestamp { get; set; } = options.Timestamp; + public int[]? vibrate { get; set; } = options.Vibrate; +} diff --git a/src/Butil/Bit.Butil/Publics/Clipboard.cs b/src/Butil/Bit.Butil/Publics/Clipboard.cs index 50235a9afb..ac252e53c6 100644 --- a/src/Butil/Bit.Butil/Publics/Clipboard.cs +++ b/src/Butil/Bit.Butil/Publics/Clipboard.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.JSInterop; -using static System.Net.Mime.MediaTypeNames; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/Crypto.cs b/src/Butil/Bit.Butil/Publics/Crypto.cs index e1b3671361..f90519c561 100644 --- a/src/Butil/Bit.Butil/Publics/Crypto.cs +++ b/src/Butil/Bit.Butil/Publics/Crypto.cs @@ -1,5 +1,5 @@ -using System.Threading.Tasks; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/ElementReferenceExtensions.cs b/src/Butil/Bit.Butil/Publics/ElementReferenceExtensions.cs index 9b28e8acc8..2610b1dd79 100644 --- a/src/Butil/Bit.Butil/Publics/ElementReferenceExtensions.cs +++ b/src/Butil/Bit.Butil/Publics/ElementReferenceExtensions.cs @@ -1,9 +1,9 @@ using System; -using System.Threading.Tasks; -using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; -using Microsoft.JSInterop; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/History.cs b/src/Butil/Bit.Butil/Publics/History.cs index 72c9543e54..e7d7df044e 100644 --- a/src/Butil/Bit.Butil/Publics/History.cs +++ b/src/Butil/Bit.Butil/Publics/History.cs @@ -1,8 +1,8 @@ using System; -using System.Linq; -using System.Threading.Tasks; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/Keyboard.cs b/src/Butil/Bit.Butil/Publics/Keyboard.cs index 1a6929b711..d12db5b4f0 100644 --- a/src/Butil/Bit.Butil/Publics/Keyboard.cs +++ b/src/Butil/Bit.Butil/Publics/Keyboard.cs @@ -1,8 +1,8 @@ using System; -using System.Linq; -using System.Threading.Tasks; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/Notification.cs b/src/Butil/Bit.Butil/Publics/Notification.cs new file mode 100644 index 0000000000..a03da5bfa1 --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/Notification.cs @@ -0,0 +1,75 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.JSInterop; + +namespace Bit.Butil; + +/// <summary> +/// This service is used to configure and display native notifications to the user. +/// <br/> +/// <see href="https://developer.mozilla.org/en-US/docs/Web/API/Notification">https://developer.mozilla.org/en-US/docs/Web/API/Notification</see> +/// </summary> +public class Notification(IJSRuntime js) +{ + /// <summary> + /// Checks if the runtime (browser or web-view) is supporting the Web Notification API. + /// </summary> + public async ValueTask<bool> IsSupported() + { + return await js.InvokeAsync<bool>("BitButil.notification.isSupported"); + } + + /// <summary> + /// Gets the current permission of the Notification API. + /// <br/> + /// <see href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission_static">https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission_static</see> + /// </summary> + public async ValueTask<NotificationPermission> GetPermission() + { + var permission = await js.InvokeAsync<string>("BitButil.notification.getPermission"); + + return permission switch + { + "denied" => NotificationPermission.Denied, + "granted" => NotificationPermission.Granted, + "default" => NotificationPermission.Default, + _ => NotificationPermission.Default + }; + } + + /// <summary> + /// Requests permission from the user for the current origin to display notifications. + /// <br/> + /// <see href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static">https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static</see> + /// </summary> + public async ValueTask<NotificationPermission> RequestPermission() + { + var permission = await js.InvokeAsync<string>("BitButil.notification.requestPermission"); + + return permission switch + { + "denied" => NotificationPermission.Denied, + "granted" => NotificationPermission.Granted, + "default" => NotificationPermission.Default, + _ => NotificationPermission.Default + }; + } + + /// <summary> + /// Requests a native notification to show to the user. + /// <br/> + /// <see href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification">https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification</see> + /// </summary> + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NotificationOptions))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(InternalNotificationOptions))] + public async ValueTask Show(string title, NotificationOptions? options = null) + { + InternalNotificationOptions? opts = null; + if (options is not null) + { + opts = new(options); + } + + await js.InvokeVoidAsync("BitButil.notification.show", title, opts); + } +} diff --git a/src/Butil/Bit.Butil/Publics/Notification/NotificationDirection.cs b/src/Butil/Bit.Butil/Publics/Notification/NotificationDirection.cs new file mode 100644 index 0000000000..caf5d426de --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/Notification/NotificationDirection.cs @@ -0,0 +1,9 @@ +namespace Bit.Butil; + +public enum NotificationDirection +{ + Auto, + Ltr, + Rtl +} + diff --git a/src/Butil/Bit.Butil/Publics/Notification/NotificationOptions.cs b/src/Butil/Bit.Butil/Publics/Notification/NotificationOptions.cs new file mode 100644 index 0000000000..f0a8726e89 --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/Notification/NotificationOptions.cs @@ -0,0 +1,72 @@ +namespace Bit.Butil; + +/// <summary> +/// An options object containing any custom settings that you want to apply to the notification. +/// </summary> +public class NotificationOptions +{ + /// <summary> + /// A string containing the URL of the image used to represent the notification when there isn't enough space to display the notification itself + /// </summary> + public string? Badge { get; set; } + + /// <summary> + /// A string representing the body text of the notification, which is displayed below the title. + /// </summary> + public string? Body { get; set; } + + /// <summary> + /// Arbitrary data that you want associated with the notification. + /// </summary> + public object? Data { get; set; } + + /// <summary> + /// The direction in which to display the notification. It defaults to auto, which just adopts the browser's language setting behavior. + /// </summary> + public NotificationDirection Dir { get; set; } + + /// <summary> + /// A string containing the URL of an icon to be displayed in the notification. + /// </summary> + public string? Icon { get; set; } + + /// <summary> + /// A string containing the URL of an image to be displayed in the notification. + /// </summary> + public string? Image { get; set; } + + /// <summary> + /// The notification's language, as specified using a string representing a language tag + /// </summary> + public string? Lang { get; set; } + + /// <summary> + /// A boolean value specifying whether the user should be notified after a new notification replaces an old one. + /// </summary> + public bool? Renotify { get; set; } + + /// <summary> + /// Indicates that a notification should remain active until the user clicks or dismisses it, rather than closing automatically. + /// </summary> + public bool RequireInteraction { get; set; } + + /// <summary> + /// A boolean value specifying whether the notification should be silent + /// </summary> + public bool? Silent { get; set; } + + /// <summary> + /// A string representing an identifying tag for the notification. + /// </summary> + public string? Tag { get; set; } + + /// <summary> + /// A timestamp, given as Unix time in milliseconds, representing the time associated with the notification. + /// </summary> + public long? Timestamp { get; set; } + + /// <summary> + /// A vibration pattern for the device's vibration hardware to emit with the notification. + /// </summary> + public int[]? Vibrate { get; set; } +} diff --git a/src/Butil/Bit.Butil/Publics/Notification/NotificationPermission.cs b/src/Butil/Bit.Butil/Publics/Notification/NotificationPermission.cs new file mode 100644 index 0000000000..76a8d29fff --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/Notification/NotificationPermission.cs @@ -0,0 +1,19 @@ +namespace Bit.Butil; + +public enum NotificationPermission +{ + /// <summary> + /// The user refuses to have notifications displayed. + /// </summary> + Denied, + + /// <summary> + /// The user accepts having notifications displayed. + /// </summary> + Granted, + + /// <summary> + /// The user choice is unknown and therefore the browser will act as if the value were denied. + /// </summary> + Default +} diff --git a/src/Butil/Bit.Butil/Publics/Screen.cs b/src/Butil/Bit.Butil/Publics/Screen.cs index ad334e7ae5..232a51f3af 100644 --- a/src/Butil/Bit.Butil/Publics/Screen.cs +++ b/src/Butil/Bit.Butil/Publics/Screen.cs @@ -1,8 +1,8 @@ using System; -using System.Linq; -using System.Threading.Tasks; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/ScreenOrientation.cs b/src/Butil/Bit.Butil/Publics/ScreenOrientation.cs index 01a3cf661a..a28fd724cd 100644 --- a/src/Butil/Bit.Butil/Publics/ScreenOrientation.cs +++ b/src/Butil/Bit.Butil/Publics/ScreenOrientation.cs @@ -1,8 +1,8 @@ using System; -using System.Linq; -using System.Threading.Tasks; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Publics/UserAgent.cs b/src/Butil/Bit.Butil/Publics/UserAgent.cs new file mode 100644 index 0000000000..efd5b4a89b --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/UserAgent.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Bit.Butil.Publics.UserAgent; +using Microsoft.JSInterop; + +namespace Bit.Butil; + +/// <summary> +/// This service is used to detect the user agent information such as the Operating System, browser or web-view, versions and properties. +/// </summary> +public class UserAgent(IJSRuntime js) +{ + /// <summary> + /// Extracts the user agent properties from the browser or web-view. + /// </summary> + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(UserAgentProperties))] + public async ValueTask<UserAgentProperties> Extract() + { + return await js.InvokeAsync<UserAgentProperties>("BitButil.userAgent.extract"); + } +} diff --git a/src/Butil/Bit.Butil/Publics/UserAgent/UserAgentProperties.cs b/src/Butil/Bit.Butil/Publics/UserAgent/UserAgentProperties.cs new file mode 100644 index 0000000000..f0de7b5b7b --- /dev/null +++ b/src/Butil/Bit.Butil/Publics/UserAgent/UserAgentProperties.cs @@ -0,0 +1,16 @@ +namespace Bit.Butil.Publics.UserAgent; + +public class UserAgentProperties +{ + public string? Name { get; set; } + public string? Version { get; set; } + public string? Prerelease { get; set; } + public string? Layout { get; set; } + public string? Manufacturer { get; set; } + public string? Product { get; set; } + public string? OsName { get; set; } + public string? OsVersion { get; set; } + public int? OsArchitecture { get; set; } + public string? Description { get; set; } + public string? UserAgentValue { get; set; } +} diff --git a/src/Butil/Bit.Butil/Publics/VisualViewport.cs b/src/Butil/Bit.Butil/Publics/VisualViewport.cs index 28b44b7779..c0407825d2 100644 --- a/src/Butil/Bit.Butil/Publics/VisualViewport.cs +++ b/src/Butil/Bit.Butil/Publics/VisualViewport.cs @@ -1,9 +1,9 @@ using System; -using System.Linq; -using System.Threading.Tasks; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Microsoft.JSInterop; namespace Bit.Butil; diff --git a/src/Butil/Bit.Butil/Scripts/butil.ts b/src/Butil/Bit.Butil/Scripts/butil.ts index 8efd232ba1..f66743e56f 100644 --- a/src/Butil/Bit.Butil/Scripts/butil.ts +++ b/src/Butil/Bit.Butil/Scripts/butil.ts @@ -1,2 +1,2 @@ var BitButil = BitButil || {}; -BitButil.version = window['bit-butil version'] = '8.11.0'; \ No newline at end of file +BitButil.version = window['bit-butil version'] = '8.12.0'; \ No newline at end of file diff --git a/src/Butil/Bit.Butil/Scripts/notification.ts b/src/Butil/Bit.Butil/Scripts/notification.ts new file mode 100644 index 0000000000..524918b5eb --- /dev/null +++ b/src/Butil/Bit.Butil/Scripts/notification.ts @@ -0,0 +1,39 @@ +var BitButil = BitButil || {}; + +(function (butil: any) { + butil.notification = { + isSupported, + getPermission, + requestPermission, + show, + }; + + function isSupported() { + return ('Notification' in window); + } + + function getPermission() { + return Notification.permission; + } + + async function requestPermission() { + return await Notification.requestPermission(); + } + + function show(title: string, options?: NotificationOptions) { + for (const key in options) { + if (options.hasOwnProperty(key)) { + options[key] = options[key] === null ? undefined : options[key]; + } + } + + try { + const notification = new Notification(title, options); + } catch (e) { + navigator.serviceWorker?.getRegistration().then(reg => { + reg.showNotification(title, options); + }); + } + } + +}(BitButil)); \ No newline at end of file diff --git a/src/Butil/Bit.Butil/Scripts/userAgent.ts b/src/Butil/Bit.Butil/Scripts/userAgent.ts new file mode 100644 index 0000000000..7f4d341a8f --- /dev/null +++ b/src/Butil/Bit.Butil/Scripts/userAgent.ts @@ -0,0 +1,595 @@ +var BitButil = BitButil || {}; + +(function (butil: any) { + butil.userAgent = { + extract, + }; + + function extract() { + var nav = window.navigator; + var ua = nav.userAgent || ''; + + var opera = (window as any).operamini || (window as any).opera; + var brave = (nav as any).brave; + + var data; + var arch = ua; + var description = []; + var prerelease = null; + var version = opera && typeof opera.version == 'function' && opera.version(); + + var layout = getLayout([ + { 'label': 'EdgeHTML', 'pattern': 'Edge' }, + 'Trident', + { 'label': 'WebKit', 'pattern': 'AppleWebKit' }, + 'iCab', + 'Presto', + 'NetFront', + 'Tasman', + 'KHTML', + 'Gecko' + ]); + var name = getName([ + 'Adobe AIR', + 'Arora', + 'Avant Browser', + 'Breach', + 'Camino', + 'Electron', + 'Epiphany', + 'Fennec', + 'Flock', + 'Galeon', + 'GreenBrowser', + 'iCab', + 'Iceweasel', + 'K-Meleon', + 'Konqueror', + 'Lunascape', + 'Maxthon', + { 'label': 'Microsoft Edge', 'pattern': '(?:Edge|Edg|EdgA|EdgiOS)' }, + 'Midori', + 'Nook Browser', + 'PaleMoon', + 'PhantomJS', + 'Raven', + 'Rekonq', + 'RockMelt', + { 'label': 'Samsung Internet', 'pattern': 'SamsungBrowser' }, + 'SeaMonkey', + { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' }, + 'Sleipnir', + 'SlimBrowser', + { 'label': 'SRWare Iron', 'pattern': 'Iron' }, + 'Sunrise', + 'Swiftfox', + 'Vivaldi', + 'Waterfox', + 'WebPositive', + { 'label': 'Yandex Browser', 'pattern': 'YaBrowser' }, + { 'label': 'UC Browser', 'pattern': 'UCBrowser' }, + 'Opera Mini', + { 'label': 'Opera Mini', 'pattern': 'OPiOS' }, + 'Opera', + { 'label': 'Opera', 'pattern': 'OPR' }, + 'Chromium', + 'Chrome', + { 'label': 'Chrome', 'pattern': '(?:HeadlessChrome)' }, + { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' }, + { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' }, + { 'label': 'Firefox for iOS', 'pattern': 'FxiOS' }, + { 'label': 'IE', 'pattern': 'IEMobile' }, + { 'label': 'IE', 'pattern': 'MSIE' }, + 'Safari' + ]); + var product = getProduct([ + { 'label': 'BlackBerry', 'pattern': 'BB10' }, + 'BlackBerry', + { 'label': 'Galaxy S', 'pattern': 'GT-I9000' }, + { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' }, + { 'label': 'Galaxy S3', 'pattern': 'GT-I9300' }, + { 'label': 'Galaxy S4', 'pattern': 'GT-I9500' }, + { 'label': 'Galaxy S5', 'pattern': 'SM-G900' }, + { 'label': 'Galaxy S6', 'pattern': 'SM-G920' }, + { 'label': 'Galaxy S6 Edge', 'pattern': 'SM-G925' }, + { 'label': 'Galaxy S7', 'pattern': 'SM-G930' }, + { 'label': 'Galaxy S7 Edge', 'pattern': 'SM-G935' }, + 'Google TV', + 'Lumia', + 'iPad', + 'iPod', + 'iPhone', + 'Kindle', + { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' }, + 'Nexus', + 'Nook', + 'PlayBook', + 'PlayStation Vita', + 'PlayStation', + 'TouchPad', + 'Transformer', + { 'label': 'Wii U', 'pattern': 'WiiU' }, + 'Wii', + 'Xbox One', + { 'label': 'Xbox 360', 'pattern': 'Xbox' }, + 'Xoom' + ]); + var manufacturer = getManufacturer({ + 'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 }, + 'Alcatel': {}, + 'Archos': {}, + 'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 }, + 'Asus': { 'Transformer': 1 }, + 'Barnes & Noble': { 'Nook': 1 }, + 'BlackBerry': { 'PlayBook': 1 }, + 'Google': { 'Google TV': 1, 'Nexus': 1 }, + 'HP': { 'TouchPad': 1 }, + 'HTC': {}, + 'Huawei': {}, + 'Lenovo': {}, + 'LG': {}, + 'Microsoft': { 'Xbox': 1, 'Xbox One': 1 }, + 'Motorola': { 'Xoom': 1 }, + 'Nintendo': { 'Wii U': 1, 'Wii': 1 }, + 'Nokia': { 'Lumia': 1 }, + 'Oppo': {}, + 'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 }, + 'Sony': { 'PlayStation': 1, 'PlayStation Vita': 1 }, + 'Xiaomi': { 'Mi': 1, 'Redmi': 1 } + }); + var os = getOS([ + 'Windows Phone', + 'KaiOS', + 'Android', + 'CentOS', + { 'label': 'Chrome OS', 'pattern': 'CrOS' }, + 'Debian', + { 'label': 'DragonFly BSD', 'pattern': 'DragonFly' }, + 'Fedora', + 'FreeBSD', + 'Gentoo', + 'Haiku', + 'Kubuntu', + 'Linux Mint', + 'OpenBSD', + 'Red Hat', + 'SuSE', + 'Ubuntu', + 'Xubuntu', + 'Cygwin', + 'Symbian OS', + 'hpwOS', + 'webOS ', + 'webOS', + 'Tablet OS', + 'Tizen', + 'Linux', + 'Mac OS X', + 'Macintosh', + 'Mac', + 'Windows 98;', + 'Windows ' + ]); + + layout && (layout = [layout]); + + // Detect Android products. + if (/\bAndroid\b/.test(os) && !product && + (data = /\bAndroid[^;]*;(.*?)(?:Build|\) AppleWebKit)\b/i.exec(ua))) { + product = trim(data[1]) + // Replace any language codes (eg. "en-US"). + .replace(/^[a-z]{2}-[a-z]{2};\s*/i, '') + || null; + } + + // Detect product names that contain their manufacturer's name. + if (manufacturer && !product) { + product = getProduct([manufacturer]); + } else if (manufacturer && product) { + product = product + .replace(RegExp('^(' + qualify(manufacturer) + ')[-_.\\s]', 'i'), manufacturer + ' ') + .replace(RegExp('^(' + qualify(manufacturer) + ')[-_.]?(\\w)', 'i'), manufacturer + ' $2'); + } + + // Detect simulators. + if (/\bSimulator\b/i.test(ua)) { + product = (product ? product + ' ' : '') + 'Simulator'; + } + + // Detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS. + if (name == 'Opera Mini' && /\bOPiOS\b/.test(ua)) { + description.push('running in Turbo/Uncompressed mode'); + } + + // Detect iOS. + else if (/^iP/.test(product)) { + name || (name = 'Safari'); + os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua)) + ? ' ' + data[1].replace(/_/g, '.') + : ''); + } + + // Detect Android browsers. + else if ((manufacturer && manufacturer != 'Google' && + ((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) || + (/\bAndroid\b/.test(os) && /^Chrome/.test(name) && /\bVersion\//i.test(ua))) { + name = 'Android Browser'; + os = /\bAndroid\b/.test(os) ? os : 'Android'; + } + + // Detect false positives for Firefox/Safari. + else if (!name || (data = !/\bMinefield\b/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) { + // Escape the `/` for Firefox 1. + if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) { + // Clear name of false positives. + name = null; + } + // Reassign a generic name. + if ((data = product || manufacturer || os) && + (product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) { + name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser'; + } + } + + // Detect non-Opera (Presto-based) versions (order is important). + if (!version) { + version = getVersion([ + '(?:Cloud9|CriOS|CrMo|Edge|Edg|EdgA|EdgiOS|FxiOS|HeadlessChrome|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|SamsungBrowser|Silk(?!/[\\d.]+$)|UCBrowser|YaBrowser)', + 'Version', + qualify(name), + '(?:Firefox|Minefield|NetFront)' + ]); + } + + // Detect stubborn layout engines. + if ((data = + layout == 'iCab' && parseFloat(version) > 3 && 'WebKit' || + /\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') || + /\b(?:Midori|Nook|Safari)\b/i.test(ua) && !/^(?:Trident|EdgeHTML)$/.test(layout) && 'WebKit' || + !layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident') || + layout == 'WebKit' && /\bPlayStation\b(?! Vita\b)/i.test(name) && 'NetFront' + )) { + layout = [data]; + } + + // Detect Firefox Mobile. + if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS|KaiOS)\b/.test(os)) { + name = 'Firefox Mobile'; + } + // Add mobile postfix. + else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) && + (os == 'Windows CE' || /Mobi/i.test(ua))) { + name += ' Mobile'; + } + + // Detect WebKit Nightly and approximate Chrome/Safari versions. + if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) { + // Correct build number for numeric comparison. + // (e.g. "532.5" becomes "532.05") + data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data]; + // Nightly builds are postfixed with a "+". + if (name == 'Safari' && data[1].slice(-1) == '+') { + name = 'WebKit Nightly'; + prerelease = 'alpha'; + version = data[1].slice(0, -1); + } + // Clear incorrect browser versions. + else if (version == data[1] || + version == (data[2] = (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1])) { + version = null; + } + // Use the full Chrome version when available. + data[1] = (/\b(?:Headless)?Chrome\/([\d.]+)/i.exec(ua) || 0)[1]; + // Detect Blink layout engine. + if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && layout == 'WebKit') { + layout = ['Blink']; + } + // Detect JavaScriptCore. + // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi + var likeChrome = /\bChrome\b/.test(ua) && !/internal|\n/i.test(Object.prototype.toString.toString()); + if ((!likeChrome && !data[1])) { + layout && (layout[1] = 'like Safari'); + data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : data < 602 ? 9 : data < 604 ? 10 : data < 606 ? 11 : data < 608 ? 12 : '12'); + } else { + layout && (layout[1] = 'like Chrome'); + data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28'); + } + // Add the postfix of ".x" or "+" for approximate versions. + layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+')); + // Obscure version for some Safari 1-2 releases. + if (name == 'Safari' && (!version || parseInt(version) > 45)) { + version = data; + } else if (name == 'Chrome' && /\bHeadlessChrome/i.test(ua)) { + description.unshift('headless'); + } + } + + // Detect Opera desktop modes. + if (name == 'Opera' && (data = /\bzbov|zvav$/.exec(os))) { + name += ' '; + description.unshift('desktop mode'); + if (data == 'zvav') { + name += 'Mini'; + version = null; + } else { + name += 'Mobile'; + } + os = os.replace(RegExp(' *' + data + '$'), ''); + } + // Detect Chrome desktop mode. + else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) { + description.unshift('desktop mode'); + name = 'Chrome Mobile'; + version = null; + + if (/\bOS X\b/.test(os)) { + manufacturer = 'Apple'; + os = 'iOS 4.3+'; + } else { + os = null; + } + } + + // Strip incorrect OS versions. + if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 && + ua.indexOf('/' + data + '-') > -1) { + os = trim(os.replace(data, '')); + } + + // Ensure OS does not include the browser name. + if (os && os.indexOf(name) != -1 && !RegExp(name + ' OS').test(os)) { + os = os.replace(RegExp(' *' + qualify(name) + ' *'), ''); + } + + // Add layout engine. + if (layout && !/\b(?:Avant|Nook)\b/.test(name) && ( + /Browser|Lunascape|Maxthon/.test(name) || + name != 'Safari' && /^iOS/.test(os) && /\bSafari\b/.test(layout[1]) || + /^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Samsung Internet|Sleipnir|SRWare Iron|Vivaldi|Web)/.test(name) && layout[1])) { + // Don't add layout details to description if they are falsey. + (data = layout[layout.length - 1]) && description.push(data); + } + + // Combine contextual information. + if (description.length) { + description = ['(' + description.join('; ') + ')']; + } + + // Append manufacturer to description. + if (manufacturer && product && product.indexOf(manufacturer) < 0) { + description.push('on ' + manufacturer); + } + + // Append product to description. + if (product) { + description.push((/^on /.test(description[description.length - 1]) ? '' : 'on ') + product); + } + + // Parse the OS into an object. + if (os) { + data = / ([\d.+]+)$/.exec(os); + var isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/'; + os = { + 'architecture': 32, + 'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os, + 'version': data ? data[1] : null, + 'toString': function () { + var version = this.version; + return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : ''); + } + }; + } + + // Add browser/OS architecture. + if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) { + if (os) { + os.architecture = 64; + os.family = os.family.replace(RegExp(' *' + data), ''); + } + if ( + name && (/\bWOW64\b/i.test(ua) || + (/\w(?:86|32)$/.test((nav as any).cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua))) + ) { + description.unshift('32-bit'); + } + } + // Chrome 39 and above on OS X is always 64-bit. + else if (os && /^OS X/.test(os.family) && name == 'Chrome' && parseFloat(version) >= 39) { + os.architecture = 64; + } + + var userAgentProps = {} as any; + userAgentProps.name = brave ? 'Brave' : name; + userAgentProps.version = name && version; + userAgentProps.prerelease = prerelease; + userAgentProps.layout = layout && layout[0]; + userAgentProps.manufacturer = manufacturer; + userAgentProps.product = product; + userAgentProps.osName = os?.family; + userAgentProps.osVersion = os?.version; + userAgentProps.osArchitecture = os?.architecture; + userAgentProps.description = ua; + userAgentProps.userAgentValue = ua; + + if (userAgentProps.version) { + description.unshift(version); + } + if (userAgentProps.name) { + description.unshift(name); + } + if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) { + description.push(product ? '(' + os + ')' : 'on ' + os); + } + if (description.length) { + userAgentProps.description = description.join(' '); + } + + return userAgentProps; + + function getLayout(guesses) { + return reduce(guesses, function (result, guess) { + return result || RegExp('\\b' + ( + guess.pattern || qualify(guess) + ) + '\\b', 'i').exec(ua) && (guess.label || guess); + }); + } + + function getManufacturer(guesses) { + return reduce(guesses, function (result, value, key) { + // Lookup the manufacturer by product or scan the UA for the manufacturer. + return result || ( + value[product] || + value[(/^[a-z]+(?: +[a-z]+\b)*/i.exec(product)) as any] || + RegExp('\\b' + qualify(key) + '(?:\\b|\\w*\\d)', 'i').exec(ua) + ) && key; + }); + } + + function getName(guesses) { + return reduce(guesses, function (result, guess) { + return result || RegExp('\\b' + ( + guess.pattern || qualify(guess) + ) + '\\b', 'i').exec(ua) && (guess.label || guess); + }); + } + + function getOS(guesses) { + return reduce(guesses, function (result, guess) { + var pattern = guess.pattern || qualify(guess); + if (!result && (result = + RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua) + )) { + result = cleanupOS(result, pattern, guess.label || guess); + } + return result; + }); + + function cleanupOS(os, pattern, label) { + var data = { + '10.0': '10', + '6.4': '10 Technical Preview', + '6.3': '8.1', + '6.2': '8', + '6.1': 'Server 2008 R2 / 7', + '6.0': 'Server 2008 / Vista', + '5.2': 'Server 2003 / XP 64-bit', + '5.1': 'XP', + '5.01': '2000 SP1', + '5.0': '2000', + '4.0': 'NT', + '4.90': 'ME' + }; + + + if (pattern && label && /^Win/i.test(os) && !/^Windows Phone /i.test(os) && + (data = data[(/[\d.]+$/.exec(os)) as any])) { + os = 'Windows ' + data; + } + + os = String(os); + + if (pattern && label) { + os = os.replace(RegExp(pattern, 'i'), label); + } + + os = format( + os.replace(/ ce$/i, ' CE') + .replace(/\bhpw/i, 'web') + .replace(/\bMacintosh\b/, 'Mac OS') + .replace(/_PowerPC\b/i, ' OS') + .replace(/\b(OS X) [^ \d]+/i, '$1') + .replace(/\bMac (OS X)\b/, '$1') + .replace(/\/(\d)/, ' $1') + .replace(/_/g, '.') + .replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '') + .replace(/\bx86\.64\b/gi, 'x86_64') + .replace(/\b(Windows Phone) OS\b/, '$1') + .replace(/\b(Chrome OS \w+) [\d.]+\b/, '$1') + .split(' on ')[0] + ); + + return os; + } + } + + function getProduct(guesses) { + return reduce(guesses, function (result, guess) { + var pattern = guess.pattern || qualify(guess); + if (!result && (result = + RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) || + RegExp('\\b' + pattern + ' *\\w+-[\\w]*', 'i').exec(ua) || + RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua) + )) { + // Split by forward slash and append product version if needed. + if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\d.]+/.test(result[0])) { + result[0] += ' ' + result[1]; + } + // Correct character case and cleanup string. + guess = guess.label || guess; + result = format(result[0] + .replace(RegExp(pattern, 'i'), guess) + .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ') + .replace(RegExp('(' + guess + ')[-_.]?(\\w)', 'i'), '$1 $2')); + } + return result; + }); + } + + function getVersion(patterns) { + return reduce(patterns, function (result, pattern) { + return result || (RegExp(pattern + + '(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null; + }); + } + } + + function capitalize(value: any) { + value = String(value); + return value.charAt(0).toUpperCase() + value.slice(1); + } + + function each(obj: any, callback: any) { + var index = -1, + length = obj ? obj.length : 0; + + if (typeof length == 'number' && length > -1 && length <= Math.pow(2, 53) - 1) { + while (++index < length) { + callback(obj[index], index, obj); + } + } else { + forOwn(obj, callback); + } + } + + function format(value: any) { + value = trim(value); + return /^(?:webOS|i(?:OS|P))/.test(value) + ? value + : capitalize(value); + } + + function forOwn(obj: any, callback: any) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + callback(obj[key], key, obj); + } + } + } + + function qualify(value: any) { + return String(value).replace(/([ -])(?!$)/g, '$1?'); + } + + function reduce(array: any, callback: any) { + var accumulator = null; + each(array, function (value: any, index: any) { + accumulator = callback(accumulator, value, index, array); + }); + return accumulator; + } + + function trim(value: any) { + return String(value).replace(/^ +| +$/g, ''); + } + +}(BitButil)); \ No newline at end of file diff --git a/src/Butil/Bit.Butil/package-lock.json b/src/Butil/Bit.Butil/package-lock.json index 8f46781b15..29c5c62a58 100644 --- a/src/Butil/Bit.Butil/package-lock.json +++ b/src/Butil/Bit.Butil/package-lock.json @@ -5,14 +5,14 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -27,9 +27,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -44,9 +44,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -61,9 +61,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -78,9 +78,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -95,9 +95,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -112,9 +112,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -129,9 +129,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -146,9 +146,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -163,9 +163,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -180,9 +180,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -197,9 +197,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -214,9 +214,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -231,9 +231,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -248,9 +248,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -265,9 +265,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -282,9 +282,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -299,9 +299,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -316,9 +316,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -350,9 +350,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -367,9 +367,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -384,9 +384,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -401,9 +401,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -431,36 +431,36 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Butil/Bit.Butil/package.json b/src/Butil/Bit.Butil/package.json index 90c59d8ad7..d1553de999 100644 --- a/src/Butil/Bit.Butil/package.json +++ b/src/Butil/Bit.Butil/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "esbuild": "0.23.1", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "typescript": "5.6.3" } } diff --git a/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/NotificationPage.razor b/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/NotificationPage.razor new file mode 100644 index 0000000000..1b0fe05f0e --- /dev/null +++ b/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/NotificationPage.razor @@ -0,0 +1,70 @@ +@page "/notification" +@inject Bit.Butil.Console console +@inject Bit.Butil.Notification notification + +<PageTitle>Notification Samples</PageTitle> + +<h1>Notification</h1> + +<pre style="font-family:Consolas"> +@@inject Bit.Butil.Notification notification + +@@code { + ... + await notification.Show("title", new() { body: "this is body" }); + ... +} +</pre> + +<br /> +<hr /> + +<h3>Open the DevTools console and start clicking on buttons</h3> + +<hr /> +<br /> + +<button @onclick="GetPermission">Get current permission</button> + +<br /> +<br /> +<hr /> +<br /> + +<button @onclick="RequestPermission">Request permission</button> + +<br /> +<br /> +<hr /> +<br /> + +<input @bind=@notifTitle /> +<br /> +<textarea @bind=@notifBody /> +<br /> +<button @onclick="Show">Show</button> + +<br /> +<br /> + +@code { + private string notifTitle; + private string notifBody; + + private async Task GetPermission() + { + var permission = await notification.GetPermission(); + await console.Log("Notification permission:", permission.ToString()); + } + + private async Task RequestPermission() + { + var permission = await notification.RequestPermission(); + await console.Log("Notification permission:", permission.ToString()); + } + + private async Task Show() + { + await notification.Show(notifTitle, new() { Body = notifBody }); + } +} \ No newline at end of file diff --git a/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/UserAgentPage.razor b/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/UserAgentPage.razor new file mode 100644 index 0000000000..237f1f7292 --- /dev/null +++ b/src/Butil/Demo/Bit.Butil.Demo.Core/Pages/UserAgentPage.razor @@ -0,0 +1,38 @@ +@page "/userAgent" +@inject Bit.Butil.Console console +@inject Bit.Butil.UserAgent userAgent + +<PageTitle>UserAgent Samples</PageTitle> + +<h1>UserAgent</h1> + +<pre style="font-family:Consolas"> +@@inject Bit.Butil.UserAgent userAgent + +@@code { + ... + var userAgentProps = await userAgent.Extract(); + ... +} +</pre> + +<br /> +<hr /> + +<h3>Open the DevTools console and start clicking on buttons</h3> + +<hr /> +<br /> + +<button @onclick="Extract">Extract</button> + +<br /> +<br /> + +@code { + private async Task Extract() + { + var userAgentProps = await userAgent.Extract(); + await console.Log("UserAgent properties:", userAgentProps); + } +} \ No newline at end of file diff --git a/src/Butil/Demo/Bit.Butil.Demo.Core/Shared/Header.razor b/src/Butil/Demo/Bit.Butil.Demo.Core/Shared/Header.razor index 168e4319c6..4b292073a3 100644 --- a/src/Butil/Demo/Bit.Butil.Demo.Core/Shared/Header.razor +++ b/src/Butil/Demo/Bit.Butil.Demo.Core/Shared/Header.razor @@ -1,19 +1,20 @@ <div class="container"> - <a href="/">Index</a> | - <a href="/window">Window</a> | - <a href="/document">Document</a> | - <a href="/keyboard">Keyboard</a> | + <a href="/clipboard">Clipboard</a> | <a href="/console">Console</a> | - <a href="/history">History</a> | + <a href="/cookie">Cookie</a> | + <a href="/crypto">Crypto</a> | + <a href="/document">Document</a> | <a href="/element">Element</a> | - <a href="/navigator">Navigator</a> | + <a href="/history">History</a> | + <a href="/keyboard">Keyboard</a> | <a href="/storage">Storage</a> | <a href="/location">Location</a> | + <a href="/navigator">Navigator</a> | + <a href="/notification">Notification</a> | <a href="/screen">Screen</a> | - <a href="/cookie">Cookie</a> | - <a href="/crypto">Crypto</a> | - <a href="/clipboard">Clipboard</a> | - <a href="/visualViewport">VisualViewport</a> | <a href="/screenOrientation">ScreenOrientation</a> + <a href="/userAgent">UserAgent</a> | + <a href="/visualViewport">VisualViewport</a> | + <a href="/window">Window</a> | </div> <hr /> \ No newline at end of file diff --git a/src/Butil/Demo/Bit.Butil.Demo.Maui/Bit.Butil.Demo.Maui.csproj b/src/Butil/Demo/Bit.Butil.Demo.Maui/Bit.Butil.Demo.Maui.csproj index dfa524c008..1411e9b38b 100644 --- a/src/Butil/Demo/Bit.Butil.Demo.Maui/Bit.Butil.Demo.Maui.csproj +++ b/src/Butil/Demo/Bit.Butil.Demo.Maui/Bit.Butil.Demo.Maui.csproj @@ -50,10 +50,9 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.82" /> - <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.82" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.82" /> - <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> + <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.93" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.93" /> + <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" /> </ItemGroup> <ItemGroup> diff --git a/src/Butil/Demo/Bit.Butil.Demo.Web/Bit.Butil.Demo.Web.csproj b/src/Butil/Demo/Bit.Butil.Demo.Web/Bit.Butil.Demo.Web.csproj index 13ffa757ad..e0447ad0a5 100644 --- a/src/Butil/Demo/Bit.Butil.Demo.Web/Bit.Butil.Demo.Web.csproj +++ b/src/Butil/Demo/Bit.Butil.Demo.Web/Bit.Butil.Demo.Web.csproj @@ -8,8 +8,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" /> </ItemGroup> <ItemGroup> diff --git a/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/index.html b/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/index.html index 5742e2110e..02b28b4087 100644 --- a/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/index.html +++ b/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/index.html @@ -10,6 +10,9 @@ <body> <div id="app"></div> + <script> + navigator.serviceWorker.register('/sw.js'); + </script> <script src="_content/Bit.Butil/bit-butil.js"></script> <script src="_framework/blazor.webassembly.js"></script> </body> diff --git a/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/sw.js b/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/sw.js new file mode 100644 index 0000000000..e0110862a6 --- /dev/null +++ b/src/Butil/Demo/Bit.Butil.Demo.Web/wwwroot/sw.js @@ -0,0 +1,3 @@ +self.addEventListener('install', e => { + console.log('sw install'); +}); \ No newline at end of file diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj index 5bbd87e33e..976cfccd71 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj @@ -1,4 +1,4 @@ -<!-- Generated by bit-empty template v-8.11.0 --> +<!-- Generated by bit-empty template v-8.12.0 --> <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> @@ -12,18 +12,18 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> + <PackageReference Include="Bit.BlazorUI" Version="8.12.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj index af6ed317ea..e81f8e4122 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj @@ -1,4 +1,4 @@ -<!-- Generated by bit-empty template v-8.11.0 --> +<!-- Generated by bit-empty template v-8.12.0 --> <Project Sdk="Microsoft.NET.Sdk.Web"> @@ -12,20 +12,20 @@ <!--#if (UseWebAssembly) --> <ItemGroup> <ProjectReference Include="..\BlazorEmpty.Client\BlazorEmpty.Client.csproj" Condition="'$(UseWebAssembly)' == 'True'" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" Condition="'$(UseWebAssembly)' == 'True'" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" Condition="'$(UseWebAssembly)' == 'True'" /> </ItemGroup> <!--#endif --> <ItemGroup> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> + <PackageReference Include="Bit.BlazorUI" Version="8.12.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/src/Templates/BlazorEmpty/UpdateLocalInstallation.bat b/src/Templates/BlazorEmpty/UpdateLocalInstallation.bat new file mode 100644 index 0000000000..5be5c95895 --- /dev/null +++ b/src/Templates/BlazorEmpty/UpdateLocalInstallation.bat @@ -0,0 +1 @@ +dotnet pack -c Release -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0 && cd bin\Release && dotnet new uninstall Bit.BlazorEmpty && dotnet new install .\Bit.BlazorEmpty.0.0.0.nupkg && cd ..\.. \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/cd.yml b/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/cd.yml index 25094d4aa5..be37a062eb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/cd.yml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/cd.yml @@ -9,7 +9,7 @@ variables: ConnectionStrings.SqlServerConnectionString: $(DB_CONNECTION_STRING) DataProtectionCertificatePassword: $(API_DATA_PROTECTION_CERTIFICATE_PASSWORD) ServerAddress: 'https://use-your-server-url-here.com/' - WindowsUpdateSettings.FilesUrl: 'https://use-your-server-url-here.com/windows' # Deploy the published Windows application files to your desired hosting location and use the host url here. + WindowsUpdate.FilesUrl: 'https://use-your-server-url-here.com/windows' # Deploy the published Windows application files to your desired hosting location and use the host url here. jobs: @@ -17,7 +17,7 @@ jobs: displayName: 'build api + blazor web' pool: - vmImage: 'ubuntu-22.04' + vmImage: 'ubuntu-24.04' steps: - task: UseDotNet@2 @@ -28,16 +28,9 @@ jobs: - task: NodeTool@1 inputs: - versionSpec: '20.x' + versionSpec: '22.x' displayName: 'Install Node.js' - # - task: Bash@3 - # displayName: 'Enable pre rendering' - # inputs: - # targetType: 'inline' - # script: | - # 'sed -i 's/public static readonly bool PrerenderEnabled = false;/public static readonly bool PrerenderEnabled = true;/g' src/Client/Boilerplate.Client.Core/Services/AppRenderMode.cs' - - task: Bash@3 displayName: 'Install wasm' inputs: @@ -50,7 +43,7 @@ jobs: inputs: fileType: 'json' folderPath: './' - targetFiles: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + targetFiles: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Web/appsettings.json' - task: Bash@3 displayName: 'Generate CSS/JS files' @@ -89,7 +82,7 @@ jobs: displayName: 'deploy api + blazor' pool: - vmImage: 'ubuntu-22.04' + vmImage: 'ubuntu-24.04' steps: @@ -160,7 +153,7 @@ jobs: - task: NodeTool@1 inputs: - versionSpec: '20.x' + versionSpec: '22.x' displayName: 'Install Node.js' - task: FileTransform@2 @@ -168,7 +161,7 @@ jobs: inputs: fileType: 'json' folderPath: './' - targetFiles: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + targetFiles: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Windows/appsettings.json' - task: Bash@3 displayName: 'Generate CSS/JS files' @@ -195,7 +188,7 @@ jobs: echo A | xcopy .\bin\publish-x64 .\publish-result /s /e /h echo A | xcopy .\bin\publish .\publish-result /s /e /h dotnet tool restore - dotnet vpk pack -u Boilerplate.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e Boilerplate.Client.Windows-x86.exe -r win-x86 --framework net8.0.8-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'Boilerplate' + dotnet vpk pack -u Boilerplate.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e Boilerplate.Client.Windows-x86.exe -r win-x86 --framework net8.0-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'Boilerplate' - task: PublishPipelineArtifact@1 displayName: Upload artifact @@ -208,7 +201,7 @@ jobs: displayName: 'build blazor hybrid (android)' pool: - vmImage: 'ubuntu-22.04' + vmImage: 'ubuntu-24.04' steps: - task: UseDotNet@2 @@ -219,7 +212,7 @@ jobs: - task: NodeTool@1 inputs: - versionSpec: '20.x' + versionSpec: '22.x' displayName: 'Install Node.js' - task: Bash@3 @@ -250,7 +243,7 @@ jobs: inputs: fileType: 'json' folderPath: './' - targetFiles: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + targetFiles: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Maui/appsettings.json' - script: | dotnet build src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj -t:BeforeBuildTasks --no-restore -c Release diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/ci.yml b/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/ci.yml index 05702be439..4f927c34be 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/ci.yml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.azure-devops/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: 'CI build' pool: - vmImage: 'ubuntu-22.04' + vmImage: 'ubuntu-24.04' steps: - task: UseDotNet@2 @@ -19,9 +19,9 @@ jobs: - task: NodeTool@1 inputs: - versionSpec: '20.x' + versionSpec: '22.x' displayName: 'Install Node.js' - + - task: Bash@3 displayName: 'Build' inputs: @@ -29,7 +29,22 @@ jobs: script: 'dotnet build Boilerplate.sln -c Release' - task: Bash@3 + displayName: 'Install Playwright' + inputs: + targetType: 'inline' + script: 'pwsh src/Tests/bin/Debug/net8.0/playwright.ps1 install --with-deps' + + - task: Bash@3 + name: test displayName: 'Test' inputs: targetType: 'inline' - script: 'dotnet test src/Tests/Boilerplate.Tests.csproj' \ No newline at end of file + script: 'dotnet test src/Tests/Boilerplate.Tests.csproj --logger AzurePipelines' + + - task: PublishPipelineArtifact@1 + displayName: 'Upload Tests Artifact' + condition: failed() + dependsOn: test + inputs: + targetPath: 'src/Tests/TestResults' + artifactName: 'tests-artifact' \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.devcontainer/devcontainer.json b/src/Templates/Boilerplate/Bit.Boilerplate/.devcontainer/devcontainer.json index 79f20ac7c4..9a54a55250 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.devcontainer/devcontainer.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ "hostRequirements": { "cpus": 4 }, - "onCreateCommand": "dotnet dev-certs https --trust && dotnet workload install wasm-tools", + "onCreateCommand": "dotnet workload install wasm-tools", "waitFor": "onCreateCommand", "customizations": { "codespaces": { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/cd.yml b/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/cd.yml index cde128ca9e..ac38c9d176 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/cd.yml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/cd.yml @@ -19,7 +19,7 @@ jobs: build_api_blazor: name: build api + blazor web - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -33,17 +33,17 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - # - name: Enable pre rendering - # run: sed -i 's/public static readonly bool PrerenderEnabled = false;/public static readonly bool PrerenderEnabled = true;/g' src/Client/Boilerplate.Client.Core/Services/AppRenderMode.cs - - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + files: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Web/appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} +#if (notifications == true) + AdsPushVapid.PublicKey: ${{ secrets.PUBLIC_VAPIDKEY }} +#endif - name: Install wasm run: cd src && dotnet workload install wasm-tools @@ -69,11 +69,12 @@ jobs: with: name: server-bundle path: ${{env.DOTNET_ROOT}}/server + include-hidden-files: true # Required for wwwroot/.well-known folder deploy_api_blazor: name: deploy api + blazor needs: build_api_blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 environment: name: 'production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} @@ -141,15 +142,15 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'src\Client\Boilerplate.Client.Core\appsettings.json, src\Shared\appsettings.json' + files: 'src\Shared\appsettings.json, src\Client\Boilerplate.Client.Core\appsettings.json, src\Client\Boilerplate.Client.Windows\appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} - WindowsUpdateSettings.FilesUrl: 'https://use-your-server-url-here.com/windows' # Deploy the published Windows application files to your desired hosting location and use the host url here. + WindowsUpdate.FilesUrl: 'https://use-your-server-url-here.com/windows' # Deploy the published Windows application files to your desired hosting location and use the host url here. - name: Generate CSS/JS files run: dotnet build src\Client\Boilerplate.Client.Core\Boilerplate.Client.Core.csproj -t:BeforeBuildTasks --no-restore -c Release @@ -170,7 +171,7 @@ jobs: echo A | xcopy .\bin\publish-x64 .\publish-result /s /e /h echo A | xcopy .\bin\publish .\publish-result /s /e /h dotnet tool restore - dotnet vpk pack -u Boilerplate.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e Boilerplate.Client.Windows-x86.exe -r win-x86 --framework net8.0.8-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'Boilerplate' + dotnet vpk pack -u Boilerplate.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e Boilerplate.Client.Windows-x86.exe -r win-x86 --framework net8.0-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'Boilerplate' - name: Upload artifact uses: actions/upload-artifact@v4 @@ -181,7 +182,7 @@ jobs: build_blazor_hybrid_android: name: build blazor hybrid (android) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -195,7 +196,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Extract Android signing key from env uses: timheuer/base64-to-file@v1.2 @@ -207,7 +208,7 @@ jobs: - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + files: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} @@ -236,7 +237,7 @@ jobs: build_blazor_hybrid_iOS: name: build blazor hybrid (iOS-macOS) - runs-on: macos-14 + runs-on: macOS-15 steps: @@ -250,16 +251,16 @@ jobs: - uses: maxim-lobanov/setup-xcode@v1.6.0 with: - xcode-version: '15.4' + xcode-version: '16.0' - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Update core appsettings.json uses: devops-actions/variable-substitution@v1.2 with: - files: 'src/Client/Boilerplate.Client.Core/appsettings.json, src/Shared/appsettings.json' + files: 'src/Shared/appsettings.json, src/Client/Boilerplate.Client.Core/appsettings.json, src/Client/Boilerplate.Client.Maui/appsettings.json' env: ServerAddress: ${{ env.SERVER_ADDRESS }} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/ci.yml b/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/ci.yml index a012d66808..720c8072c6 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/ci.yml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.github/workflows/ci.yml @@ -11,13 +11,13 @@ jobs: build_blazor_server: name: build blazor - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - + - name: Checkout source code uses: actions/checkout@v4 - + - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -25,10 +25,22 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 - + node-version: 22 + - name: Build run: dotnet build Boilerplate.sln -c Release + - name: Install Playwright + run: pwsh src/Tests/bin/Debug/net8.0/playwright.ps1 install --with-deps + - name: Test - run: dotnet test src/Tests/Boilerplate.Tests.csproj \ No newline at end of file + id: test + run: dotnet test src/Tests/Boilerplate.Tests.csproj --logger GitHubActions + + - name: Upload Tests Artifact + uses: actions/upload-artifact@v4.4.1 + if: ${{ failure() && steps.test.conclusion == 'failure' }} + with: + name: tests-artifact + path: ./src/Tests/TestResults + retention-days: 14 \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.gitignore b/src/Templates/Boilerplate/Bit.Boilerplate/.gitignore index 948c6adc9a..8ee6de04ab 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.gitignore +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.gitignore @@ -227,5 +227,6 @@ custom.aprof /src/**/App_Data/* /src/Client/Boilerplate.Client.Core/wwwroot/scripts/app*.js +/src/Client/Boilerplate.Client.Maui/Platforms/Android/google-services.json .env \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/ide.host.json b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/ide.host.json index a3758ccbc1..605387827b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/ide.host.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/ide.host.json @@ -30,7 +30,7 @@ "id": "sample" }, { - "id": "signalr" + "id": "signalR" }, { "id": "offlineDb" @@ -44,9 +44,16 @@ { "id": "appCenter" }, + { + "id": "notification" + }, { "id": "serverUrl", "isVisible": false + }, + { + "id": "advancedTests", + "isVisible": false } ] } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json index 779dabd726..f197dd2b8c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json @@ -114,7 +114,7 @@ "displayName": "Captcha", "type": "parameter", "datatype": "choice", - "defaultValue": "reCaptcha", + "defaultValue": "None", "choices": [ { "choice": "reCaptcha", @@ -166,13 +166,20 @@ } ] }, - "signalr": { + "signalR": { "displayName": "Add SignalR?", "type": "parameter", "datatype": "bool", "defaultValue": "false", "description": "Add SignalR sample." }, + "notification": { + "displayName": "Add push notification?", + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Add push notification." + }, "offlineDb": { "displayName": "Add Offline db?", "type": "parameter", @@ -286,6 +293,13 @@ ] }, "replaces": "use-your-server-url-here.com" + }, + "advancedTests": { + "displayName": "Include advanced automated tests?", + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Include advanced automated tests." } }, "postActions": [ @@ -318,7 +332,7 @@ "path": "README.md" }, { - "path": "Boilerplate.sln" + "path": "Boilerplate.Web.slnf" } ], "sources": [ @@ -375,9 +389,9 @@ "src/Shared/Controllers/Categories/**", "src/Shared/Controllers/Products/**", "src/Shared/Controllers/Dashboard/**", - "src/Client/Boilerplate.Client.Core/Components/Pages/Categories/**", - "src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/**", - "src/Client/Boilerplate.Client.Core/Components/Pages/Products/**" + "src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/**", + "src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/**", + "src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/**" ] }, { @@ -388,20 +402,46 @@ "src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs", "src/Server/Boilerplate.Server.Api/Models/Todo/**", "src/Shared/Controllers/Todo/**", - "src/Client/Boilerplate.Client.Core/Components/Pages/Todo/**" + "src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/**" ] }, { "condition": "(offlineDb != true)", "exclude": [ "src/Client/Boilerplate.Client.Core/Data/**", - "src/Client/Boilerplate.Client.Core/Components/Pages/Offline/**" + "src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/**" ] }, { - "condition": "(signalr != true)", + "condition": "(signalR != true)", "exclude": [ - "src/Server/Boilerplate.Server.Api/Hubs/**" + "src/Server/Boilerplate.Server.Api/SignalR/**", + "src/Shared/Services/SharedPubSubMessages.cs", + "src/Client/Boilerplate.Client.Core/Services/SignalRInfinitiesRetryPolicy.cs" + ] + }, + { + "condition": "(notification != true)", + "exclude": [ + "src/Client/Boilerplate.Client.Core/Services/Contracts/IPushNotificationService.cs", + "src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/PushNotificationFirebaseMessagingService.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/NSDataExtensions.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/AppUNUserNotificationCenterDelegate.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/Windows/Services/WindowsPushNotificationService.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/NSDataExtensions.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/AppUNUserNotificationCenterDelegate.cs", + "src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs", + "src/Client/Boilerplate.Client.Web/Services/WebPushNotificationService.cs", + "src/Client/Boilerplate.Client.Windows/Services/WindowsPushNotificationService.cs", + "src/Server/Boilerplate.Server.Api/Models/PushNotification/**", + "src/Server/Boilerplate.Server.Api/Controllers/PushNotification/**", + "src/Server/Boilerplate.Server.Api/Services/PushNotificationService.cs", + "src/Shared/Controllers/PushNotification/**", + "src/Shared/Dtos/PushNotification/**", + "src/Server/Boilerplate.Server.Api/Mappers/PushNotificationMapper.cs" ] }, { @@ -415,14 +455,16 @@ "exclude": [ "src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaHttpClient.cs", "src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaVerificationResponse.cs", - "src/Client/Boilerplate.Client.Core/Components/Pages/Identity/GoogleRecaptcha.razor" + "src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/GoogleRecaptcha.razor", + "src/Tests/Services/FakeGoogleRecaptchaHttpClient.cs" ] }, { "condition": "(appInsights != true)", "exclude": [ - "src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs", - "src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs" + "src/Client/Boilerplate.Client.Maui/Services/MauiAppInsightsTelemetryInitializer.cs", + "src/Client/Boilerplate.Client.Windows/Services/WindowsAppInsightsTelemetryInitializer.cs", + "src/Client/Boilerplate.Client.Core/Services/AppInsightsJsSdkService.cs" ] }, { @@ -442,6 +484,25 @@ "exclude": [ "src/Server/Boilerplate.Server.Web/appsettings*.json" ] + }, + { + "condition": "(advancedTests != true)", + "exclude": [ + "src/Tests/PageTests/**", + "src/Tests/Services/CulturedStringLocalizer.cs", + "src/Tests/Services/EmailReaderService.cs", + "src/Tests/Services/UserService.cs", + "src/Tests/Services/FakePhoneService.cs", + "src/Tests/Services/FakeGoogleRecaptchaHttpClient.cs", + "src/Tests/Extensions/PlaywrightCacheExtensions.cs" + ] + }, + { + "condition": "(advancedTests == true)", + "exclude": [ + "src/Tests/IdentityApiTests.cs", + "src/Tests/IdentityPagesTests.cs" + ] } ] } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/settings.json b/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/settings.json index 4ab3d9b391..741797f20b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/settings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/settings.json @@ -1,6 +1,7 @@ { "liveSassCompile.settings.watchOnLaunch": true, "dotnet.defaultSolution": "Boilerplate.Web.slnf", + "dotnet.unitTests.runSettingsPath": "src/Tests/.runsettings", "csharp.preview.improvedLaunchExperience": true, "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.Web.slnf b/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.Web.slnf index 29612b5aa8..4285b9b1f3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.Web.slnf +++ b/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.Web.slnf @@ -2,11 +2,11 @@ "solution": { "path": "Boilerplate.sln", "projects": [ - "src\\Server\\Boilerplate.Server.Web\\Boilerplate.Server.Web.csproj", - "src\\Server\\Boilerplate.Server.Api\\Boilerplate.Server.Api.csproj", - "src\\Shared\\Boilerplate.Shared.csproj", "src\\Client\\Boilerplate.Client.Core\\Boilerplate.Client.Core.csproj", "src\\Client\\Boilerplate.Client.Web\\Boilerplate.Client.Web.csproj", + "src\\Server\\Boilerplate.Server.Api\\Boilerplate.Server.Api.csproj", + "src\\Server\\Boilerplate.Server.Web\\Boilerplate.Server.Web.csproj", + "src\\Shared\\Boilerplate.Shared.csproj", "src\\Tests\\Boilerplate.Tests.csproj" ] } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.sln b/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.sln index bcca3e8ed9..0fe0c6d09d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.sln +++ b/src/Templates/Boilerplate/Bit.Boilerplate/Boilerplate.sln @@ -9,8 +9,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".SolutionItems", ".Solution .editorconfig = .editorconfig .gitignore = .gitignore .vsconfig = .vsconfig + settings.VisualStudio.json = settings.VisualStudio.json Clean.bat = Clean.bat src\Directory.Build.props = src\Directory.Build.props + src\Directory.Packages.props = src\Directory.Packages.props global.json = global.json README.md = README.md EndProjectSection diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/global.json b/src/Templates/Boilerplate/Bit.Boilerplate/global.json index 5273746f66..9df6ea197b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/global.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.400", + "version": "8.0.402", "rollForward": "latestFeature" } } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json b/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json new file mode 100644 index 0000000000..bbaede9ccd --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/settings.VisualStudio.json @@ -0,0 +1,4 @@ +/* Visual Studio Settings File */ +{ + "debugging.general.disableJITOptimization": true +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj index f15ac06f16..e387af0cd5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj @@ -16,22 +16,23 @@ <Content Remove="appsettings*.json" /> <EmbeddedResource Include="appsettings*.json" /> - <PackageReference Include="Bit.Butil" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI.Icons" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI.Assets" Version="8.11.0" /> + <PackageReference Include="Bit.Butil" /> + <PackageReference Include="Bit.BlazorUI" /> + <PackageReference Include="Bit.BlazorUI.Icons" /> + <PackageReference Include="Bit.BlazorUI.Assets" /> <!--/+:msbuild-conditional:noEmit --> - <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Bit.Besql" Version="8.11.0" /> - <PackageReference Condition=" '$(signalr)' == 'true' OR '$(signalr)' == ''" Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.8" /> - <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> - <PackageReference Include="Bit.BlazorUI.Extras" Version="8.11.0" /> + <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="BlazorApplicationInsights" /> + <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Bit.Besql" /> + <PackageReference Condition=" '$(signalR)' == 'true' OR '$(signalR)' == ''" Include="Microsoft.AspNetCore.SignalR.Client" /> + <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Sqlite" /> + <PackageReference Include="Bit.BlazorUI.Extras" /> <!--/-:msbuild-conditional:noEmit --> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> - <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="8.0.0" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" /> + <PackageReference Include="Microsoft.Extensions.Logging.Configuration" /> + <PackageReference Include="Microsoft.Extensions.Logging.Debug" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" /> <Using Include="System.Net.Http.Json" /> <Using Include="System.Collections.Concurrent" /> @@ -67,13 +68,13 @@ <Exec Command="npm install" StandardOutputImportance="high" StandardErrorImportance="high" /> </Target> - <Target Name="BuildJavaScript" Inputs="@(TypeScriptFiles)" Outputs="wwwroot\scripts\app.js"> + <Target Name="BuildJavaScript" Inputs="@(TypeScriptFiles);tsconfig.json;package.json" Outputs="wwwroot\scripts\app.js"> <Exec Command="node_modules/.bin/tsc" StandardOutputImportance="high" StandardErrorImportance="high" /> - <Exec Condition=" '$(Configuration)' == 'Release' " Command="node_modules/.bin/esbuild wwwroot/scripts/app.js --minify --outfile=wwwroot/scripts/app.js --allow-overwrite" StandardOutputImportance="high" StandardErrorImportance="high" /> + <Exec Condition=" '$(Environment)' == 'Production' " Command="node_modules/.bin/esbuild wwwroot/scripts/app.js --minify --outfile=wwwroot/scripts/app.js --allow-overwrite" StandardOutputImportance="high" StandardErrorImportance="high" /> </Target> - <Target Name="BuildCssFiles"> - <Exec Command="node_modules/.bin/sass .:. Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Target Name="BuildCssFiles"> + <Exec Command="node_modules/.bin/sass .:. Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/ClientCoreSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/ClientCoreSettings.cs new file mode 100644 index 0000000000..5335c210c1 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/ClientCoreSettings.cs @@ -0,0 +1,31 @@ +//+:cnd:noEmit + +namespace Boilerplate.Client.Core; + +public partial class ClientCoreSettings : SharedSettings +{ + /// <summary> + /// If you're running Boilerplate.Server.Web project, then you can also use relative urls such as / for Blazor Server and WebAssembly + /// </summary> + [Required] + public string ServerAddress { get; set; } = default!; + + //#if (captcha == "reCaptcha") + [Required] + public string GoogleRecaptchaSiteKey { get; set; } = default!; + //#endif + + public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = base.Validate(validationContext).ToList(); + + //#if (captcha == "reCaptcha") + if (AppEnvironment.IsDev() is false && GoogleRecaptchaSiteKey is "6LdMKr4pAAAAAKMyuEPn3IHNf04EtULXA8uTIVRw") + { + validationResults.Add(new ValidationResult("Please set your own GoogleRecaptchaSiteKey in Client.Core's appsettings.json")); + } + //#endif + + return validationResults; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs index d086d52e92..596f43d88a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs @@ -1,12 +1,14 @@ -using System.Text.Json; +using System.Runtime.CompilerServices; namespace Boilerplate.Client.Core.Components; public partial class AppComponentBase : ComponentBase, IAsyncDisposable { - [CascadingParameter] public Task<AuthenticationState> AuthenticationStateTask { get; set; } = default!; - - + /// <summary> + /// <inheritdoc cref="Parameters.IsOnline"/> + /// </summary> + [CascadingParameter(Name = Parameters.IsOnline)] protected bool? IsOnline { get; set; } + [CascadingParameter] protected Task<AuthenticationState> AuthenticationStateTask { get; set; } = default!; [AutoInject] protected IJSRuntime JSRuntime = default!; @@ -22,9 +24,9 @@ public partial class AppComponentBase : ComponentBase, IAsyncDisposable [AutoInject] protected IPrerenderStateService PrerenderStateService = default!; /// <summary> - /// <inheritdoc cref="IPubSubService"/> + /// <inheritdoc cref="Services.PubSubService"/> /// </summary> - [AutoInject] protected IPubSubService PubSubService = default!; + [AutoInject] protected PubSubService PubSubService = default!; [AutoInject] protected IConfiguration Configuration = default!; @@ -38,13 +40,15 @@ public partial class AppComponentBase : ComponentBase, IAsyncDisposable [AutoInject] protected AuthenticationManager AuthenticationManager = default!; + [AutoInject] protected SnackBarService SnackBarService = default!; + + [AutoInject] protected ITelemetryContext TelemetryContext = default!; private readonly CancellationTokenSource cts = new(); protected CancellationToken CurrentCancellationToken => cts.Token; - protected bool InPrerenderSession => JSRuntime.IsInitialized() is false; - + protected bool InPrerenderSession => AppPlatform.IsBlazorHybrid is false && JSRuntime.IsInitialized() is false; protected sealed override void OnInitialized() { @@ -60,7 +64,7 @@ protected sealed override async Task OnInitializedAsync() } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp); } } @@ -82,7 +86,7 @@ protected sealed override async Task OnParametersSetAsync() } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp); } } @@ -105,7 +109,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp); } } @@ -124,7 +128,10 @@ protected virtual Task OnAfterFirstRenderAsync() /// <summary> /// Executes passed action while catching all possible exceptions to prevent app crash. /// </summary> - public virtual Action WrapHandled(Action action) + public virtual Action WrapHandled(Action action, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") { return () => { @@ -134,7 +141,7 @@ public virtual Action WrapHandled(Action action) } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp, null, lineNumber, memberName, filePath); } }; } @@ -142,7 +149,10 @@ public virtual Action WrapHandled(Action action) /// <summary> /// Executes passed action while catching all possible exceptions to prevent app crash. /// </summary> - public virtual Action<T> WrapHandled<T>(Action<T> func) + public virtual Action<T> WrapHandled<T>(Action<T> func, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") { return (e) => { @@ -152,7 +162,7 @@ public virtual Action<T> WrapHandled<T>(Action<T> func) } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp, null, lineNumber, memberName, filePath); } }; } @@ -160,7 +170,10 @@ public virtual Action<T> WrapHandled<T>(Action<T> func) /// <summary> /// Executes passed function while catching all possible exceptions to prevent app crash. /// </summary> - public virtual Func<Task> WrapHandled(Func<Task> func) + public virtual Func<Task> WrapHandled(Func<Task> func, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") { return async () => { @@ -170,7 +183,7 @@ public virtual Func<Task> WrapHandled(Func<Task> func) } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp, null, lineNumber, memberName, filePath); } }; } @@ -178,7 +191,10 @@ public virtual Func<Task> WrapHandled(Func<Task> func) /// <summary> /// Executes passed function while catching all possible exceptions to prevent app crash. /// </summary> - public virtual Func<T, Task> WrapHandled<T>(Func<T, Task> func) + public virtual Func<T, Task> WrapHandled<T>(Func<T, Task> func, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") { return async (e) => { @@ -188,7 +204,7 @@ public virtual Func<T, Task> WrapHandled<T>(Func<T, Task> func) } catch (Exception exp) { - ExceptionHandler.Handle(exp); + HandleException(exp, null, lineNumber, memberName, filePath); } }; } @@ -204,8 +220,22 @@ protected virtual async ValueTask DisposeAsync(bool disposing) { if (disposing) { - cts.Cancel(); - cts.Dispose(); + await PrerenderStateService.DisposeAsync(); + cts?.Cancel(); + cts?.Dispose(); } } + + private void HandleException(Exception exp, + Dictionary<string, object?>? parameters = null, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") + { + parameters ??= []; + + parameters["ComponentType"] = GetType().FullName; + + ExceptionHandler.Handle(exp, parameters, lineNumber, memberName, filePath); + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppDataAnnotationsValidator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppDataAnnotationsValidator.cs index d53dd76f27..9b1044f72d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppDataAnnotationsValidator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppDataAnnotationsValidator.cs @@ -52,7 +52,7 @@ private void OnFieldChanged(object? sender, FieldChangedEventArgs eventArgs) var results = new List<ValidationResult>(); var parent = propertyInfo.DeclaringType!; - var dtoResourceTypeAttr = parent.GetCustomAttribute<DtoResourceTypeAttribute>(); + var dtoResourceTypeAttr = parent.GetCustomAttribute<DtoResourceTypeAttribute>(inherit: true); if (dtoResourceTypeAttr is not null) { var resourceType = dtoResourceTypeAttr.ResourceType; @@ -117,7 +117,7 @@ private void OnValidationRequested(object? sender, ValidationRequestedEventArgs var objectType = modelValidationContext.ObjectType; var objectInstance = modelValidationContext.ObjectInstance; - var dtoResourceTypeAttr = objectType.GetCustomAttribute<DtoResourceTypeAttribute>(); + var dtoResourceTypeAttr = objectType.GetCustomAttribute<DtoResourceTypeAttribute>(inherit: true); validationMessageStore.Clear(); if (dtoResourceTypeAttr is not null) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs deleted file mode 100644 index 12b91b9ac0..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs +++ /dev/null @@ -1,157 +0,0 @@ -//+:cnd:noEmit -using Microsoft.Extensions.Logging; -//#if (signalr == true) -using Microsoft.AspNetCore.SignalR.Client; -//#endif - -namespace Boilerplate.Client.Core.Components; - -public partial class AppInitializer : AppComponentBase -{ - //#if (signalr == true) - private HubConnection? hubConnection; - //#endif - - [AutoInject] private MessageBoxService messageBoxService = default!; - [AutoInject] private AuthenticationManager authManager = default!; - [AutoInject] private IJSRuntime jsRuntime = default!; - [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; - [AutoInject] private IStorageService storageService = default!; - [AutoInject] private CultureInfoManager cultureInfoManager = default!; - [AutoInject] private ILogger<AuthenticationManager> authLogger = default!; - - protected async override Task OnInitAsync() - { - AuthenticationManager.AuthenticationStateChanged += AuthenticationStateChanged; - - AuthenticationStateChanged(AuthenticationManager.GetAuthenticationStateAsync()); - - if (AppPlatform.IsBlazorHybrid) - { - if (CultureInfoManager.MultilingualEnabled) - { - cultureInfoManager.SetCurrentCulture(await storageService.GetItem("Culture") ?? // 1- User settings - CultureInfo.CurrentUICulture.Name); // 2- OS settings - } - - await SetupBodyClasses(); - } - - await base.OnInitAsync(); - } - - protected override async Task OnAfterFirstRenderAsync() - { - await base.OnAfterFirstRenderAsync(); - - if (AppPlatform.IsBlazorHybrid is false) - { - AppPlatform.OSDescription = await jsRuntime.GetBrowserPlatform(); - } - } - - private async void AuthenticationStateChanged(Task<AuthenticationState> task) - { - try - { - var user = (await AuthenticationStateTask).User; - - var (isUserAuthenticated, userId, userName, email, sessionId) = user.IsAuthenticated() ? (user.IsAuthenticated(), user.GetUserId().ToString(), user.GetUserName(), user.GetEmail(), user.GetSessionId()) : default; - - LogAuthenticationState(authLogger, isUserAuthenticated, userId, userName, email, sessionId); - - //#if (signalr == true) - if (InPrerenderSession is false) - { - await ConnectSignalR(); - } - //#endif - } - catch (Exception exp) - { - ExceptionHandler.Handle(exp); - } - } - - [LoggerMessage(Level = LogLevel.Information, Message = "Authentication State: {IsUserAuthenticated}, {UserId}, {UserName}, {Email}, {UserSessionId}")] - private static partial void LogAuthenticationState(ILogger logger, bool isUserAuthenticated, string userId, string userName, string? email, string? userSessionId); - - //#if (signalr == true) - private async Task ConnectSignalR() - { - if (hubConnection is not null) - { - await hubConnection.DisposeAsync(); - } - - var access_token = await AuthTokenProvider.GetAccessTokenAsync(); - - hubConnection = new HubConnectionBuilder() - .WithUrl($"{Configuration.GetServerAddress()}/app-hub?access_token={access_token}") - .Build(); - - hubConnection.On<string>("TwoFactorToken", async (token) => - { - await messageBoxService.Show(Localizer[nameof(AppStrings.TwoFactorTokenPushText), token]); - - // The following code block is not required for Bit.BlazorUI components to perform UI changes. However, it may be necessary in other scenarios. - /*await InvokeAsync(async () => - { - StateHasChanged(); - });*/ - - // You can also leverage IPubSubService to notify other components in the application. - }); - - await hubConnection.StartAsync(CurrentCancellationToken); - } - //#endif - - private async Task SetupBodyClasses() - { - var cssClasses = new List<string> { }; - - if (AppPlatform.IsWindows) - { - cssClasses.Add("bit-windows"); - } - else if (AppPlatform.IsMacOS) - { - cssClasses.Add("bit-macos"); - } - else if (AppPlatform.IsIOS) - { - cssClasses.Add("bit-ios"); - } - else if (AppPlatform.IsAndroid) - { - cssClasses.Add("bit-android"); - } - - var cssVariables = new Dictionary<string, string>(); - var statusBarHeight = bitDeviceCoordinator.GetStatusBarHeight(); - - if (AppPlatform.IsMacOS is false) - { - //For iOS this is handled in css using safe-area env() variables - //For Android there's an issue with keyboard in fullscreen mode. more info: https://github.com/bitfoundation/bitplatform/issues/5626 - //For Windows there's an issue with TitleBar. more info: https://github.com/bitfoundation/bitplatform/issues/5695 - statusBarHeight = 0; - } - - cssVariables.Add("--bit-status-bar-height", $"{statusBarHeight.ToString("F3", CultureInfo.InvariantCulture)}px"); - await jsRuntime.ApplyBodyElementClasses(cssClasses, cssVariables); - } - - //#if (signalr == true) - protected override async ValueTask DisposeAsync(bool disposing) - { - if (hubConnection is not null) - { - await hubConnection.DisposeAsync(); - } - - await base.DisposeAsync(disposing); - } - //#endif -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppRouteDataPublisher.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppRouteDataPublisher.cs new file mode 100644 index 0000000000..7ae7359013 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppRouteDataPublisher.cs @@ -0,0 +1,14 @@ + +namespace Boilerplate.Client.Core.Components; + +public partial class AppRouteDataPublisher : AppComponentBase +{ + [Parameter] public RouteData? RouteData { get; set; } + + protected override Task OnInitAsync() + { + PubSubService.Publish(ClientPubSubMessages.ROUTE_DATA_UPDATED, RouteData); + + return base.OnInitAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs new file mode 100644 index 0000000000..8425ae9932 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs @@ -0,0 +1,261 @@ +//+:cnd:noEmit +using Microsoft.Extensions.Logging; +//#if (signalR == true) +using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.AspNetCore.Http.Connections; +//#endif +//#if (appInsights == true) +using BlazorApplicationInsights.Interfaces; +//#endif + +namespace Boilerplate.Client.Core.Components; + +/// <summary> +/// Manages the initialization and coordination of core services and settings within the client application. +/// This includes authentication state handling, telemetry setup, culture configuration, and optional +/// services such as SignalR connections, push notifications, and application insights. +/// </summary> +public partial class ClientAppCoordinator : AppComponentBase +{ + //#if (signalR == true) + private HubConnection? hubConnection; + [AutoInject] private Notification notification = default!; + //#endif + //#if (notification == true) + [AutoInject] private IPushNotificationService pushNotificationService = default!; + //#endif + //#if (appInsights == true) + [AutoInject] private IApplicationInsights appInsights = default!; + //#endif + [AutoInject] private Navigator navigator = default!; + [AutoInject] private IJSRuntime jsRuntime = default!; + [AutoInject] private IStorageService storageService = default!; + [AutoInject] private ILogger<ClientAppCoordinator> logger = default!; + [AutoInject] private AuthenticationManager authManager = default!; + [AutoInject] private CultureInfoManager cultureInfoManager = default!; + [AutoInject] private ILogger<AuthenticationManager> authLogger = default!; + [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; + + protected override async Task OnInitAsync() + { + AuthenticationManager.AuthenticationStateChanged += AuthenticationStateChanged; + + if (InPrerenderSession is false) + { + TelemetryContext.UserAgent = await navigator.GetUserAgent(); + TelemetryContext.TimeZone = await jsRuntime.GetTimeZone(); + TelemetryContext.Culture = CultureInfo.CurrentCulture.Name; + if (AppPlatform.IsBlazorHybrid is false) + { + TelemetryContext.OS = await jsRuntime.GetBrowserPlatform(); + } + + //#if (appInsights == true) + _ = appInsights.AddTelemetryInitializer(new() + { + Data = new() + { + ["ai.application.ver"] = TelemetryContext.AppVersion, + ["ai.session.id"] = TelemetryContext.AppSessionId, + ["ai.device.locale"] = TelemetryContext.Culture + } + }); + //#endif + + AuthenticationStateChanged(AuthenticationManager.GetAuthenticationStateAsync()); + } + + if (AppPlatform.IsBlazorHybrid) + { + if (CultureInfoManager.MultilingualEnabled) + { + cultureInfoManager.SetCurrentCulture(new Uri(NavigationManager.Uri).GetCulture() ?? // 1- Culture query string OR Route data request culture + await storageService.GetItem("Culture") ?? // 2- User settings + CultureInfo.CurrentUICulture.Name); // 3- OS settings + } + + await SetupBodyClasses(); + } + + await base.OnInitAsync(); + } + + private async void AuthenticationStateChanged(Task<AuthenticationState> task) + { + try + { + var user = (await task).User; + var isAuthenticated = user.IsAuthenticated(); + TelemetryContext.UserId = isAuthenticated ? user.GetUserId() : null; + TelemetryContext.UserSessionId = isAuthenticated ? user.GetSessionId() : null; + + var data = TelemetryContext.ToDictionary(); + + //#if (appInsights == true) + if (isAuthenticated) + { + _ = appInsights.SetAuthenticatedUserContext(user.GetUserId().ToString()); + } + else + { + _ = appInsights.ClearAuthenticatedUserContext(); + } + //#endif + + using var scope = authLogger.BeginScope(data); + { + authLogger.LogInformation("Authentication state changed."); + } + + //#if (notification == true) + await pushNotificationService.RegisterDevice(CurrentCancellationToken); + //#endif + + //#if (signalR == true) + await ConnectSignalR(); + //#endif + } + catch (Exception exp) + { + ExceptionHandler.Handle(exp); + } + } + + //#if (signalR == true) + private async Task ConnectSignalR() + { + if (hubConnection is not null) + { + await hubConnection.DisposeAsync(); + } + + hubConnection = new HubConnectionBuilder() + .WithAutomaticReconnect(new SignalRInfinitiesRetryPolicy()) + .WithUrl($"{HttpClient.BaseAddress}app-hub", options => + { + options.Transports = HttpTransportType.WebSockets; + options.SkipNegotiation = options.Transports is HttpTransportType.WebSockets; + // Avoid enabling long polling or Server-Sent Events. Focus on resolving the issue with WebSockets instead. + // WebSockets should be enabled on services like IIS or Cloudflare CDN, offering significantly better performance. + options.AccessTokenProvider = async () => await AuthTokenProvider.GetAccessToken(); + }) + .Build(); + + hubConnection.On<string>(SignalREvents.SHOW_MESSAGE, async (message) => + { + if (await notification.IsNotificationAvailable()) + { + // Show local notification + // Note that this code has nothing to do with push notification. + await notification.Show("Boilerplate", new() { Body = message }); + } + else + { + SnackBarService.Show("Boilerplate", message); + } + + // The following code block is not required for Bit.BlazorUI components to perform UI changes. However, it may be necessary in other scenarios. + /*await InvokeAsync(async () => + { + StateHasChanged(); + });*/ + + // You can also leverage IPubSubService to notify other components in the application. + }); + + hubConnection.On<string>(SignalREvents.PUBLISH_MESSAGE, async (message) => + { + logger.LogInformation("Message {Message} received from server.", message); + PubSubService.Publish(message); + }); + + try + { + await hubConnection.StartAsync(CurrentCancellationToken); + await HubConnectionConnected(null); + } + catch (Exception exp) + { + await HubConnectionDisconnected(exp); + } + finally + { + hubConnection.Closed += HubConnectionDisconnected; + hubConnection.Reconnected += HubConnectionConnected; + hubConnection.Reconnecting += HubConnectionDisconnected; + } + } + + private async Task HubConnectionConnected(string? connectionId) + { + PubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, true); + logger.LogInformation("SignalR connection {ConnectionId} established.", connectionId); + } + + private async Task HubConnectionDisconnected(Exception? exception) + { + PubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, false); + + if (exception is null) + { + logger.LogInformation("SignalR connection lost."); // Was triggered intentionally by either server or client. + } + else + { + if (exception is HubException && exception.Message.EndsWith(nameof(AppStrings.UnauthorizedException))) + { + await AuthenticationManager.RefreshToken(); + } + + logger.LogError(exception, "SignalR connection lost."); + } + } + + //#endif + + private async Task SetupBodyClasses() + { + var cssClasses = new List<string> { }; + + if (AppPlatform.IsWindows) + { + cssClasses.Add("bit-windows"); + } + else if (AppPlatform.IsMacOS) + { + cssClasses.Add("bit-macos"); + } + else if (AppPlatform.IsIOS) + { + cssClasses.Add("bit-ios"); + } + else if (AppPlatform.IsAndroid) + { + cssClasses.Add("bit-android"); + } + + var cssVariables = new Dictionary<string, string> + { + }; + + await jsRuntime.ApplyBodyElementClasses(cssClasses, cssVariables); + } + + protected override async ValueTask DisposeAsync(bool disposing) + { + AuthenticationManager.AuthenticationStateChanged -= AuthenticationStateChanged; + + //#if (signalR == true) + if (hubConnection is not null) + { + hubConnection.Closed -= HubConnectionDisconnected; + hubConnection.Reconnected -= HubConnectionConnected; + hubConnection.Reconnecting -= HubConnectionDisconnected; + await hubConnection.DisposeAsync(); + } + //#endif + + await base.DisposeAsync(disposing); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor index 3f44f3d2a3..b7478d4588 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor @@ -4,27 +4,33 @@ { @ChildContent } -else if (ErrorContent is null) +else if (ErrorContent is not null) { - <div class="main"> - <div class="header"> - <BitButton OnClick="Recover" IconName="@BitIconName.ChromeClose" Variant="BitVariant.Text" /> - </div> - <div> - <img src="_content/Boilerplate.Client.Core/images/icons/error-triangle.svg" /> - </div> - <h1 class="title">Oops, something went wrong...</h1> - @if (showException) - { - <div class="exception">@CurrentException?.ToString()</div> - } - <div class="buttons"> - <BitButton OnClick="Refresh">Refresh</BitButton> - <BitButton OnClick="GoHome" Variant="BitVariant.Outline">Home</BitButton> - </div> - </div> + @ErrorContent(CurrentException) } else { - @ErrorContent(CurrentException) + <main> + <BitStack Alignment="BitAlignment.Center"> + <BitImage Src="_content/Boilerplate.Client.Core/images/icons/error-triangle.svg" /> + + <BitText Color="BitColor.Error" Typography="BitTypography.H3"> + @localizer[nameof(AppStrings.SomethingWentWrong)] + </BitText> + + @if (showException) + { + <div class="exception"> + @CurrentException?.ToString() + </div> + } + + <BitStack Horizontal Alignment="BitAlignment.Center" AutoHeight> + <BitButton OnClick="Refresh">@localizer[nameof(AppStrings.Refresh)]</BitButton> + <BitButton OnClick="GoHome" Variant="BitVariant.Outline">@localizer[nameof(AppStrings.Home)]</BitButton> + <BitButton OnClick="Recover" Variant="BitVariant.Outline">@localizer[nameof(AppStrings.Recover)]</BitButton> + <BitButton OnClick="ShowDiagnostic" Variant="BitVariant.Text" IconOnly IconName="@BitIconName.Diagnostic" /> + </BitStack> + </BitStack> + </main> } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.cs index 973ba7e39c..76896d95f5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.cs @@ -9,9 +9,10 @@ public partial class AppErrorBoundary { private bool showException; + [AutoInject] private PubSubService pubSubService = default!; [AutoInject] private IExceptionHandler exceptionHandler = default!; - [AutoInject] private NavigationManager navigationManager = default!; + [AutoInject] private IStringLocalizer<AppStrings> localizer = default!; protected override void OnInitialized() { @@ -30,6 +31,11 @@ private void Refresh() private void GoHome() { - navigationManager.NavigateTo(Urls.HomePage, true); + navigationManager.NavigateTo(Urls.HomePage, forceLoad: true); + } + + private void ShowDiagnostic() + { + pubSubService.Publish(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.scss index dccdeb59b8..60e6469f77 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppErrorBoundary.razor.scss @@ -1,33 +1,14 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; -.main { +main { width: 100%; - display: flex; - padding: rem2(8px); - text-align: center; - max-width: rem2(800px); - max-height: rem2(600px); - flex-direction: column; - border-radius: rem2(4px); - background-color: $bit-color-background-primary; -} - -.header { - text-align: end; -} - -.title { - color: $bit-color-error; + height: 100%; } .exception { + width: 90%; + margin: 1.5rem; overflow: auto; white-space: pre; text-align: start; - margin: rem2(24px); -} - -.buttons { - margin-bottom: rem2(24px); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor new file mode 100644 index 0000000000..504d51e8c8 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor @@ -0,0 +1,29 @@ +@inherits AppComponentBase + +<header> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitButton IconOnly FixedColor + Class="menu-btn" + Size="BitSize.Large" + OnClick="OpenNavPanel" + Variant="BitVariant.Text" + Color="BitColor.SecondaryBackground" + IconName="@BitIconName.CollapseMenu" /> + + <BitStack AutoSize VerticalAlign="BitAlignment.Center" Gap="0"> + <BitText Typography="BitTypography.H5"> + @pageTitle + </BitText> + @if (string.IsNullOrWhiteSpace(pageSubtitle) is false) + { + <BitText Typography="BitTypography.Body1" Foreground="BitColorKind.Secondary" NoWrap> + @pageSubtitle + </BitText> + } + </BitStack> + + <DiagnosticSpacer /> + + <UserMenu /> + </BitStack> +</header> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.cs new file mode 100644 index 0000000000..f54775c3c9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.cs @@ -0,0 +1,33 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class AuthorizedHeader : AppComponentBase +{ + private string? pageTitle; + private string? pageSubtitle; + private Action unsubscribePageTitleChanged = default!; + + + protected override async Task OnInitAsync() + { + unsubscribePageTitleChanged = PubSubService.Subscribe(ClientPubSubMessages.PAGE_TITLE_CHANGED, async payload => + { + (pageTitle, pageSubtitle) = (ValueTuple<string?, string?>)payload!; + + StateHasChanged(); + }); + } + + + private void OpenNavPanel() + { + PubSubService.Publish(ClientPubSubMessages.OPEN_NAV_PANEL); + } + + + protected override async ValueTask DisposeAsync(bool disposing) + { + await base.DisposeAsync(disposing); + + unsubscribePageTitleChanged?.Invoke(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.scss new file mode 100644 index 0000000000..30beacdbed --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AuthorizedHeader.razor.scss @@ -0,0 +1,19 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +header { + top: 0; + z-index: 1; + width: 100%; + position: sticky; + padding-block: 0.5rem; + background-color: $bit-color-background-primary; +} + +::deep { + .menu-btn { + @include gt-sm { + display: none; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor deleted file mode 100644 index 919e1e5f6f..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor +++ /dev/null @@ -1,22 +0,0 @@ -@inherits AppComponentBase; - -<BitModal @bind-IsOpen="isOpen" IsBlocking="true" AutoToggleScroll="false"> - <div class="main-container"> - <div class="header"> - <span class="title">@title</span> - <BitButton IconName="@BitIconName.ChromeClose" OnClick="@(() => Confirm(false))" Variant="BitVariant.Text" /> - </div> - <div class="body"> - @message - </div> - <div class="btn-group"> - <BitButton Style="margin-inline-end: 0.5rem" OnClick="@(() => Confirm(true))"> - @Localizer[nameof(AppStrings.Yes)] - </BitButton> - - <BitButton Variant="BitVariant.Outline" OnClick="@(() => Confirm(false))"> - @Localizer[nameof(AppStrings.No)] - </BitButton> - </div> - </div> -</BitModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.cs deleted file mode 100644 index a0bdcb6263..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Boilerplate.Client.Core.Components.Layout; - -public partial class ConfirmMessageBox -{ - private bool isOpen; - private string? title; - private string? message; - - public async Task<bool> Show(string message, string title) - { - if (tcs is not null) - await tcs.Task; - - tcs = new TaskCompletionSource<bool>(); - - await InvokeAsync(() => - { - isOpen = true; - this.title = title; - this.message = message; - - StateHasChanged(); - }); - - return await tcs.Task; - } - - private TaskCompletionSource<bool>? tcs; - - public async Task Confirm(bool value) - { - isOpen = false; - tcs?.SetResult(value); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.scss deleted file mode 100644 index c2d93adad1..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/ConfirmMessageBox.razor.scss +++ /dev/null @@ -1,44 +0,0 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.main-container { - flex-grow: 1; - display: flex; - align-items: center; - flex-flow: column nowrap; - justify-content: flex-start; - padding: rem2(16px) rem2(24px); - background-color: $bit-color-background-primary; -} - -.header { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - margin-bottom: rem2(20px); - justify-content: space-between; -} - -.title { - font-weight: 600; - font-size: rem2(20px); - line-height: rem2(28px); -} - -.body { - width: 100%; - font-weight: 400; - font-size: rem2(14px); - line-height: rem2(20px); - margin-bottom: rem2(16px); -} - -.btn-group { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - justify-content: flex-end; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor new file mode 100644 index 0000000000..e9e4138769 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor @@ -0,0 +1,74 @@ +@inherits AppComponentBase + +<div> + <BitModal @bind-IsOpen="isOpen" FullSize Class="modal"> + <BitStack FillContent Class="container"> + <BitStack Horizontal AutoHeight VerticalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H3">Diagnostic</BitText> + <BitSpacer /> + <BitButton IconOnly + Color="BitColor.Info" + Variant="BitVariant.Text" + OnClick="() => isOpen = false" + IconName="@BitIconName.ChromeClose" /> + </BitStack> + + <BitAccordion Title="Telemetry Context"> + <BitButton IconOnly + Color="BitColor.Info" + OnClick="CopyTelemetry" + Variant="BitVariant.Text" + IconName="@BitIconName.Copy" /> + <BitScrollablePane> + <BitStack> + @foreach (var (key, value) in telemetryContext.ToDictionary()) + { + <BitText NoWrap><b>@key</b>: @value</BitText> + } + </BitStack> + </BitScrollablePane> + </BitAccordion> + + <BitStack Horizontal FitHeight Wrap> + <BitSearchBox Immediate DebounceTime="500" OnChange="HandleOnSearchChange" /> + <BitDropdown FitWidth MultiSelect + Items="logLevelItems" + DefaultValues="defaultFilterLogLevels" + OnValuesChange="HandleOnLogLevelFilter" + TItem="BitDropdownItem<LogLevel>" TValue="LogLevel" /> + <BitButton IconOnly Color="BitColor.SecondaryBackground" Style="height:32px;" + OnClick="HandleOnSortClick" + IconName="@(isDescendingSort ? BitIconName.SortDown : BitIconName.SortUp)" /> + <BitButton OnClick="ClearLogs" + Color="BitColor.Info" + Variant="BitVariant.Outline">Clear logs</BitButton> + </BitStack> + + <BitBasicList @ref="logStackRef" + Style="height:unset" + EnableVirtualization + Items="filteredLogs.Indexed().ToArray()"> + <EmptyContent>Nothing to show!</EmptyContent> + <RowTemplate Context="logIndex"> + <BitStack @key="logIndex.item.CreatedOn" Horizontal AutoHeight Gap="0" VerticalAlign="BitAlignment.Center"> + <BitText Style="min-width:7rem">@($"{logIndex.index + 1}. [{logIndex.item.CreatedOn.ToString("HH:mm:ss")}]")</BitText> + <BitButton IconOnly + Title="Copy" + Color="BitColor.Info" + Variant="BitVariant.Text" + IconName="@BitIconName.Copy" + OnClick="() => CopyException(logIndex.item)" /> + <BitText Style="white-space:nowrap" Color="GetColor(logIndex.item.Level)">@logIndex.item.Message</BitText> + </BitStack> + </RowTemplate> + </BitBasicList> + </BitStack> + + <BitButton IconOnly + OnClick="GoTop" + Color="BitColor.Info" + Class="go-top-button" + Variant="BitVariant.Text" + IconName="@BitIconName.Up" /> + </BitModal> +</div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs new file mode 100644 index 0000000000..c6d91f0a44 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs @@ -0,0 +1,119 @@ +using Boilerplate.Client.Core.Services.DiagnosticLog; +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Components.Layout; + +/// <summary> +/// This modal can be opened by clicking 7 times on the spacer of the header or by pressing Ctrl+Shift+X. +/// Also by calling `App.showDiagnostic` function using the dev-tools console. +/// </summary> +public partial class DiagnosticModal : IDisposable +{ + private bool isOpen; + private string? searchText; + private bool isDescendingSort = true; + private Action unsubscribe = default!; + private IEnumerable<LogLevel> filterLogLevels = []; + private IEnumerable<DiagnosticLog> allLogs = default!; + private IEnumerable<DiagnosticLog> filteredLogs = default!; + private BitBasicList<(DiagnosticLog, int)> logStackRef = default!; + private readonly BitDropdownItem<LogLevel>[] logLevelItems = Enum.GetValues<LogLevel>().Select(v => new BitDropdownItem<LogLevel>() { Value = v, Text = v.ToString() }).ToArray(); + private readonly LogLevel[] defaultFilterLogLevels = AppEnvironment.IsDev() ? [LogLevel.Information, LogLevel.Warning, LogLevel.Error, LogLevel.Critical] : [LogLevel.Warning, LogLevel.Error, LogLevel.Critical]; + + + [AutoInject] private Clipboard clipboard = default!; + [AutoInject] private ITelemetryContext telemetryContext = default!; + + + protected override Task OnInitAsync() + { + unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL, async _ => + { + isOpen = true; + allLogs = [.. DiagnosticLogger.Store]; + HandleOnLogLevelFilter(defaultFilterLogLevels); + await InvokeAsync(StateHasChanged); + }); + + return base.OnInitAsync(); + } + + + private void HandleOnSearchChange(string? text) + { + searchText = text; + FilterLogs(); + } + + private void HandleOnLogLevelFilter(IEnumerable<LogLevel> logLevels) + { + filterLogLevels = logLevels; + FilterLogs(); + } + + private void HandleOnSortClick() + { + isDescendingSort = !isDescendingSort; + FilterLogs(); + } + + private void FilterLogs() + { + filteredLogs = allLogs.WhereIf(string.IsNullOrEmpty(searchText) is false, l => l.Message?.Contains(searchText!, StringComparison.InvariantCultureIgnoreCase) is true) + .Where(l => filterLogLevels.Contains(l.Level)); + if (isDescendingSort) + { + filteredLogs = filteredLogs.OrderByDescending(l => l.CreatedOn); + } + else + { + filteredLogs = filteredLogs.OrderBy(l => l.CreatedOn); + } + } + + private async Task CopyTelemetry() + { + await clipboard.WriteText(string.Join(Environment.NewLine, telemetryContext.ToDictionary().Select(c => $"{c.Key}: {c.Value}"))); + } + + private async Task CopyException(DiagnosticLog log) + { + var stateToCopy = string.Join(Environment.NewLine, log.State?.Select(i => $"{i.Key}: {i.Value}") ?? []); + + await clipboard.WriteText($"{log.Message}{Environment.NewLine}{log.Exception?.ToString()}{Environment.NewLine}{stateToCopy}"); + } + + private async Task GoTop() + { + await logStackRef.RootElement.Scroll(0, 0); + } + + private async Task ClearLogs() + { + DiagnosticLogger.Store.Clear(); + allLogs = []; + FilterLogs(); + } + + + private static BitColor GetColor(LogLevel level) + { + return level switch + { + LogLevel.Trace => BitColor.PrimaryForeground, + LogLevel.Debug => BitColor.PrimaryForeground, + LogLevel.Information => BitColor.Primary, + LogLevel.Warning => BitColor.Warning, + LogLevel.Error => BitColor.Error, + LogLevel.Critical => BitColor.Error, + LogLevel.None => BitColor.SecondaryForeground, + _ => BitColor.TertiaryForeground + }; + } + + + public void Dispose() + { + unsubscribe?.Invoke(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss new file mode 100644 index 0000000000..1d1a732cc7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss @@ -0,0 +1,31 @@ +@import '../../Styles/abstracts/_media-queries.scss'; + +section { + width: 100%; + height: 100%; +} + +::deep { + .modal { + width: unset; + top: var(--app-inset-top); + left: var(--app-inset-left); + right: var(--app-inset-right); + bottom: var(--app-inset-bottom); + } + + .container { + padding: 1rem; + } + + .stack { + overflow: auto; + } + + .go-top-button { + left: 50%; + position: fixed; + transform: translateX(-50%); + bottom: calc(var(--app-inset-bottom) + 1rem); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticSpacer.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticSpacer.razor new file mode 100644 index 0000000000..f23930a108 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticSpacer.razor @@ -0,0 +1,15 @@ +@inherits AppComponentBase + +<BitSpacer @onclick="HandleOnClick" Style="height:100%" /> + +@code { + private int clickCount = 0; + private async Task HandleOnClick() + { + if (++clickCount == 7) + { + clickCount = 0; + PubSubService.Publish(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL); + } + } +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor deleted file mode 100644 index a2a0524141..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor +++ /dev/null @@ -1,47 +0,0 @@ -@inherits AppComponentBase - -<footer class="footer"> - @if (SelectedCulture is not null) - { - <BitDropdown @bind-Value="SelectedCulture" - Class="culture-drp" - Items="cultures" - OnSelectItem="WrapHandled((BitDropdownItem<string> item) => OnCultureChanged())" /> - } - - <div class="footer-content"> - Made with <span class="text--red">♥</span> by bit platform - - <div class="footer-social-lnk-grp"> - <BitLink Href="https://www.linkedin.com/company/bitplatformhq" Target="_blank"> - <div class="social-lnk linkedin-lnk"> - <svg viewBox="0 0 32 32"><path d="M23.155 23.13H20.2737V18.622C20.2737 17.5464 20.2556 16.164 18.7768 16.164C17.2778 16.164 17.0488 17.3354 17.0488 18.5443V23.13H14.1702V13.8586H16.9322V15.1265H16.9725C17.3562 14.3978 18.2965 13.6289 19.6984 13.6289C22.6158 13.6289 23.1543 15.5484 23.1543 18.0453V23.13H23.155ZM10.9218 12.5928C10.7022 12.593 10.4847 12.5499 10.2818 12.466C10.0789 12.382 9.89452 12.2589 9.73924 12.1036C9.58397 11.9483 9.46083 11.764 9.37688 11.561C9.29293 11.3581 9.24982 11.1407 9.25 10.9211C9.25 10.5906 9.34797 10.2676 9.53153 9.99282C9.71509 9.71804 9.97599 9.50386 10.2813 9.37734C10.5865 9.25082 10.9225 9.21764 11.2466 9.28201C11.5707 9.34637 11.8684 9.50539 12.1022 9.73896C12.336 9.97252 12.4952 10.2701 12.5599 10.5942C12.6245 10.9183 12.5916 11.2542 12.4653 11.5596C12.3391 11.865 12.1251 12.1261 11.8505 12.3098C11.5759 12.4936 11.2529 12.5919 10.9225 12.5921L10.9218 12.5928ZM12.3631 23.13H9.47901V13.8586H12.3631V23.13Z" /></svg> - </div> - </BitLink> - <BitLink Href="https://github.com/bitfoundation/bitplatform" Target="_blank"> - <div class="social-lnk github-lnk"> - <svg viewBox="0 0 32 32"><path d="M18.5373 10.0257C17.3046 9.73929 16.0226 9.73929 14.7899 10.0257C14.078 9.58908 13.5344 9.3884 13.1391 9.30369C12.9704 9.26561 12.7978 9.24767 12.6248 9.25024C12.5461 9.25237 12.4678 9.26183 12.3909 9.27848L12.3808 9.28049L12.3767 9.28251H12.3737L12.5119 9.76757L12.3737 9.28352C12.3029 9.30354 12.2373 9.33883 12.1815 9.38692C12.1258 9.435 12.0812 9.49471 12.051 9.56185C11.7535 10.2281 11.6968 10.9771 11.8907 11.6806C11.3886 12.289 11.115 13.0538 11.1172 13.8427C11.1172 15.4088 11.5791 16.4616 12.3667 17.1382C12.9183 17.6122 13.5879 17.8643 14.2595 18.0085C14.1486 18.3161 14.1203 18.6418 14.1425 18.9746V19.5776C13.7321 19.6633 13.4477 19.6361 13.245 19.5696C12.9919 19.4859 12.7973 19.3175 12.6107 19.0754C12.5132 18.945 12.4223 18.8097 12.3384 18.67L12.2809 18.5763C12.2084 18.4556 12.133 18.3365 12.0551 18.2193C11.8635 17.9359 11.5791 17.5809 11.1192 17.4599L10.6311 17.3318L10.375 18.308L10.8631 18.4361C10.9438 18.4562 11.0486 18.5319 11.2211 18.785C11.2846 18.8788 11.3441 18.9766 11.4127 19.0895L11.4813 19.2005C11.576 19.3538 11.685 19.5232 11.811 19.6886C12.0661 20.0213 12.4141 20.3571 12.9304 20.5276C13.2833 20.6446 13.6847 20.6728 14.1425 20.6022V22.4849C14.1425 22.6187 14.1956 22.7469 14.2902 22.8415C14.3848 22.936 14.513 22.9892 14.6467 22.9892H18.6805C18.8142 22.9892 18.9425 22.936 19.037 22.8415C19.1316 22.7469 19.1847 22.6187 19.1847 22.4849V18.8919C19.1847 18.5742 19.1706 18.2828 19.0808 18.0115C19.7494 17.8703 20.414 17.6182 20.9626 17.1443C21.7491 16.4626 22.21 15.3997 22.21 13.8245V13.8235C22.2075 13.0411 21.9338 12.2838 21.4355 11.6806C21.6291 10.9774 21.5725 10.2288 21.2752 9.56286C21.2452 9.49564 21.2009 9.4358 21.1453 9.38754C21.0897 9.33929 21.0242 9.30378 20.9535 9.28352L20.8153 9.76757C20.9535 9.28352 20.9525 9.28352 20.9515 9.28352L20.9495 9.28251L20.9454 9.28049L20.9363 9.27848C20.9114 9.272 20.8862 9.26695 20.8607 9.26335C20.8079 9.25564 20.7547 9.25127 20.7014 9.25024C20.5284 9.24769 20.3558 9.26563 20.1871 9.30369C19.7928 9.3884 19.2492 9.58908 18.5373 10.0257Z" /></svg> - </div> - </BitLink> - <BitLink Href="@("https://www.youtube.com/@bitplatform")" Target="_blank"> - <div class="social-lnk youtube-lnk"> - <svg viewBox="0 0 32 32"><path d="M23.6572 11.8735C24 13.21 24 16 24 16C24 16 24 18.79 23.6572 20.1265C23.4667 20.8653 22.9095 21.4465 22.2037 21.643C20.922 22 16.5 22 16.5 22C16.5 22 12.0802 22 10.7962 21.643C10.0875 21.4435 9.531 20.863 9.34275 20.1265C9 18.79 9 16 9 16C9 16 9 13.21 9.34275 11.8735C9.53325 11.1348 10.0905 10.5535 10.7962 10.357C12.0802 10 16.5 10 16.5 10C16.5 10 20.922 10 22.2037 10.357C22.9125 10.5565 23.469 11.137 23.6572 11.8735ZM15 18.625L19.5 16L15 13.375V18.625Z" /></svg> - </div> - </BitLink> - <BitLink Href="https://twitter.com/bitplatformhq" Target="_blank"> - <div class="social-lnk twitter-lnk"> - <svg viewBox="0 0 32 32"><path d="M22.9075 11.692C22.4053 11.9145 21.8657 12.0649 21.2985 12.1329C21.8838 11.7827 22.3216 11.2315 22.5304 10.5822C21.9805 10.9088 21.3787 11.1387 20.7511 11.2619C20.3291 10.8113 19.7701 10.5126 19.161 10.4123C18.5518 10.3119 17.9266 10.4155 17.3823 10.7069C16.8381 10.9983 16.4052 11.4612 16.151 12.0238C15.8968 12.5864 15.8355 13.2172 15.9765 13.8183C14.8623 13.7623 13.7724 13.4727 12.7774 12.9683C11.7824 12.4639 10.9046 11.7558 10.201 10.8902C9.96036 11.3052 9.82202 11.7864 9.82202 12.2989C9.82175 12.7602 9.93536 13.2145 10.1528 13.6214C10.3702 14.0283 10.6847 14.3753 11.0683 14.6315C10.6234 14.6173 10.1883 14.4971 9.79916 14.2808V14.3169C9.79912 14.9639 10.0229 15.5911 10.4326 16.0919C10.8424 16.5927 11.4127 16.9364 12.0469 17.0645C11.6342 17.1762 11.2014 17.1927 10.7814 17.1126C10.9603 17.6694 11.3089 18.1562 11.7783 18.505C12.2477 18.8539 12.8144 19.0471 13.3991 19.0579C12.4065 19.837 11.1807 20.2597 9.91886 20.2578C9.69533 20.2579 9.472 20.2448 9.25 20.2187C10.5309 21.0423 12.0219 21.4794 13.5447 21.4777C18.6995 21.4777 21.5175 17.2083 21.5175 13.5055C21.5175 13.3852 21.5144 13.2637 21.509 13.1434C22.0572 12.747 22.5303 12.2561 22.9063 11.6938L22.9075 11.692Z" /></svg> - </div> - </BitLink> - </div> - </div> - - <div class="theme-container"> - <button @onclick="WrapHandled(ToggleTheme)" class="toggle-theme-btn dark-theme" title="Turn on light"> - <span class="icon-container"><i class="bit-icon bit-icon--Sunny"></i></span> - </button> - <button @onclick="WrapHandled(ToggleTheme)" class="toggle-theme-btn light-theme" title="Turn off light"> - <span class="icon-container"><i class="bit-icon bit-icon--ClearNight"></i></span> - </button> - </div> -</footer> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.cs deleted file mode 100644 index 3c29ff9b1d..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.cs +++ /dev/null @@ -1,57 +0,0 @@ -//-:cnd:noEmit -namespace Boilerplate.Client.Core.Components.Layout; - -public partial class Footer -{ - [AutoInject] private Cookie cookie = default!; - [AutoInject] private IPubSubService pubSubService = default!; - [AutoInject] private BitThemeManager bitThemeManager = default!; - [AutoInject] private CultureInfoManager cultureInfoManager = default!; - [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; - - private BitDropdownItem<string>[] cultures = default!; - - protected override async Task OnInitAsync() - { - if (CultureInfoManager.MultilingualEnabled) - { - cultures = CultureInfoManager.SupportedCultures - .Select(sc => new BitDropdownItem<string> { Value = sc.Culture.Name, Text = sc.DisplayName }) - .ToArray(); - - SelectedCulture = CultureInfo.CurrentUICulture.Name; - } - - await base.OnInitAsync(); - } - - private string? SelectedCulture; - - private async Task OnCultureChanged() - { - if (AppPlatform.IsBlazorHybrid) - { - await StorageService.SetItem("Culture", SelectedCulture, persistent: true); - cultureInfoManager.SetCurrentCulture(SelectedCulture!); - pubSubService.Publish(PubSubMessages.CULTURE_CHANGED, SelectedCulture); - } - else - { - await cookie.Set(new() - { - Name = ".AspNetCore.Culture", - Value = Uri.EscapeDataString($"c={SelectedCulture}|uic={SelectedCulture}"), - MaxAge = 30 * 24 * 3600, - Path = "/", - Secure = AppEnvironment.IsDev() is false - }); - } - - NavigationManager.NavigateTo(NavigationManager.GetUriWithoutCulture(), forceLoad: true, replace: true); - } - - private async Task ToggleTheme() - { - await bitDeviceCoordinator.ApplyTheme(await bitThemeManager.ToggleDarkLightAsync() == "dark"); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.scss deleted file mode 100644 index 289aee9a04..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Footer.razor.scss +++ /dev/null @@ -1,102 +0,0 @@ -@import '../../Styles/abstracts/_vars.scss'; -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.footer { - bottom: 0; - z-index: 1; - width: 100%; - display: flex; - padding: 0.5rem; - position: fixed; - align-items: center; - height: $footerHeight; - justify-content: center; - background-color: $bit-color-background-primary; - - .bit-ios & { - padding-bottom: env(safe-area-inset-bottom); - height: calc($footerHeight + env(safe-area-inset-bottom)); - } -} - -.footer-content { - flex-grow: 1; - text-align: center; - font-size: rem2(12px); - - @include sm { - font-size: rem2(10px); - } -} - -.footer-social-lnk-grp { - display: flex; - align-items: center; - flex-flow: row nowrap; - justify-content: center; -} - -.social-lnk { - width: rem2(32px); - height: rem2(32px); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - - @include sm { - width: rem2(30px); - height: rem2(30px); - } - - svg path { - fill: $bit-color-foreground-secondary; - } -} - -::deep .culture-drp { - width: rem2(104px); -} - -.theme-container { - text-align: end; - width: rem2(104px); -} - -.toggle-theme-btn { - padding: 0; - border: none; - cursor: pointer; - height: rem2(35px); - border-radius: 50%; - min-width: rem2(35px); - color: $bit-color-primary-text; - background-color: $bit-color-primary; - - .icon-container { - height: 100%; - display: flex; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - - .bit-icon { - margin: 0 rem2(4px); - } - } - - &.dark-theme { - .icon-container { - padding: 2px 0 0 1px; - } - } -} - -.theme-dark .light-theme { - display: none; -} - -.theme-light .dark-theme { - display: none; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor deleted file mode 100644 index e665330524..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor +++ /dev/null @@ -1,25 +0,0 @@ -@inherits AppComponentBase - -<header class="header"> - <div class="header-contact"> - @if (isUserAuthenticated) - { - <BitButton Class="header-menu" IconName="@BitIconName.CollapseMenu" OnClick="ToggleMenu" Variant="BitVariant.Text" /> - } - <a class="logo-lnk" href="@Urls.HomePage"></a> - </div> - <div class="header-contact"> - @if (isUserAuthenticated is false) - { - <BitActionButton Class="header-item" IconName="@BitIconName.EntityExtraction" Href="@Urls.TermsPage" Target="_self"> - @Localizer[nameof(AppStrings.TermsTitle)] - </BitActionButton> - <BitActionButton Class="header-item" IconName="@BitIconName.AddFriend" Href="@Urls.SignUpPage" Target="_self"> - @Localizer[nameof(AppStrings.SignUp)] - </BitActionButton> - <BitActionButton Class="header-item" IconName="@BitIconName.Signin" Href="@Urls.SignInPage" Target="_self"> - @Localizer[nameof(AppStrings.SignIn)] - </BitActionButton> - } - </div> -</header> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.cs deleted file mode 100644 index 8595aabdfe..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Boilerplate.Client.Core.Components.Layout; - -public partial class Header -{ - private bool disposed; - private bool isUserAuthenticated; - - [Parameter] public EventCallback OnToggleMenu { get; set; } - - protected override async Task OnInitAsync() - { - AuthenticationManager.AuthenticationStateChanged += VerifyUserIsAuthenticatedOrNot; - - isUserAuthenticated = await PrerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.IsAuthenticated()); - - await base.OnInitAsync(); - } - - async void VerifyUserIsAuthenticatedOrNot(Task<AuthenticationState> task) - { - try - { - isUserAuthenticated = (await task).User.IsAuthenticated(); - } - catch (Exception ex) - { - ExceptionHandler.Handle(ex); - } - finally - { - await InvokeAsync(StateHasChanged); - } - } - - private async Task ToggleMenu() - { - await OnToggleMenu.InvokeAsync(); - } - - protected override async ValueTask DisposeAsync(bool disposing) - { - await base.DisposeAsync(disposing); - - if (disposed || disposing is false) return; - - AuthenticationManager.AuthenticationStateChanged -= VerifyUserIsAuthenticatedOrNot; - - disposed = true; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.scss deleted file mode 100644 index 3798ff2e37..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/Header.razor.scss +++ /dev/null @@ -1,65 +0,0 @@ -@import '../../Styles/abstracts/_vars.scss'; -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.header { - top: 0; - z-index: 1; - width: 100%; - display: flex; - padding: 0.5rem; - position: fixed; - align-items: end; - flex-flow: row nowrap; - justify-content: space-between; - background-color: $bit-color-background-primary; - padding-top: calc(0.5rem + var(--bit-status-bar-height)); - height: calc($headerHeight + var(--bit-status-bar-height)); - - .bit-ios & { - height: calc($headerHeight + env(safe-area-inset-top)); - } -} - -.logo-lnk { - width: 2rem; - height: 2rem; - cursor: pointer; - margin: 0 0.5rem; - border-radius: rem2(4px); - background-size: contain; - background-position: center; - background-repeat: no-repeat; - background-image: url('images/bit-logo.svg'); -} - -.header-contact { - display: flex; - align-items: center; - flex-flow: row nowrap; - justify-content: flex-start; -} - -::deep { - .header-menu { - display: none; - - @include lt-lg { - display: flex; - } - } - - .header-item { - display: flex; - padding: rem2(4px) rem2(16px); - - @include lt-lg { - padding: rem2(4px) rem2(8px); - } - - @include lt-md { - padding: rem2(4px) rem2(4px); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor new file mode 100644 index 0000000000..da0914fb06 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor @@ -0,0 +1,63 @@ +@inherits AppComponentBase + +<header> + <BitStack Horizontal Gap="0.5rem" VerticalAlign="BitAlignment.Center"> + @if (isCrossLayoutPage is true) + { + <BitButton Href="@Urls.SignUpPage"> + @Localizer[nameof(AppStrings.SignUp)] + </BitButton> + <BitButton Href="@Urls.SignInPage" Variant="BitVariant.Text" Color="BitColor.Tertiary"> + @Localizer[nameof(AppStrings.SignIn)] + </BitButton> + } + else if (backLinkPayload is null) + { + <BitLink NoUnderline Href="@Urls.HomePage"> + <BitStack Horizontal Gap="0.5rem" VerticalAlign="BitAlignment.Center"> + <BitIcon IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBackMirrored : BitIconName.ChromeBack)" Size="BitSize.Small" /> + <BitText Typography="BitTypography.Body2">@Localizer[nameof(AppStrings.BackToHome)]</BitText> + </BitStack> + </BitLink> + } + else + { + <BitLink NoUnderline OnClick="HandleBackLinkClick"> + <BitStack Horizontal Gap="0.5rem" VerticalAlign="BitAlignment.Center"> + <BitIcon IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBackMirrored : BitIconName.ChromeBack)" Size="BitSize.Small" /> + <BitText Typography="BitTypography.Body2">@Localizer[nameof(AppStrings.Back)]</BitText> + </BitStack> + </BitLink> + } + + <DiagnosticSpacer /> + + <BitButton IconOnly + FixedColor + Variant="BitVariant.Text" + OnClick="WrapHandled(ToggleTheme)" + Color="BitColor.SecondaryBackground" + IconName="@(currentTheme == AppThemeType.Light ? BitIconName.Sunny : BitIconName.ClearNight)" /> + + @if (CultureInfoManager.MultilingualEnabled) + { + <BitDropdown Items="cultures" Responsive + FitWidth NoBorder Transparent + Classes="@(new() { Callout="language-callout" })" + DefaultValue="@CultureInfo.CurrentUICulture.Name" + OnChange="WrapHandled((string c) => OnCultureChanged(c))"> + <TextTemplate Context="item"> + <BitImage Src="@($"_content/Boilerplate.Client.Core/images/flags/{item.Value}.webp")" /> + </TextTemplate> + <ItemTemplate Context="item"> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitImage Src="@($"_content/Boilerplate.Client.Core/images/flags/{item.Value}.webp")" /> + <BitText Typography="BitTypography.Body1" Style="@(item.IsSelected ? "font-weight:bold" : "")"> + @item.Text + </BitText> + </BitStack> + </ItemTemplate> + </BitDropdown> + } + </BitStack> +</header> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.cs new file mode 100644 index 0000000000..f6fa704dec --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.cs @@ -0,0 +1,59 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class IdentityHeader : AppComponentBase, IDisposable +{ + private string? backLinkPayload; + private BitDropdownItem<string>[] cultures = default!; + private Action unsubscribeUpdateBackLink = default!; + + + [AutoInject] private ThemeService themeService = default!; + [AutoInject] private CultureService cultureService = default!; + + + [CascadingParameter] private BitDir? currentDir { get; set; } + [CascadingParameter(Name = Parameters.CurrentTheme)] private AppThemeType? currentTheme { get; set; } + [CascadingParameter(Name = Parameters.IsCrossLayoutPage)] private bool? isCrossLayoutPage { get; set; } + + + protected override async Task OnInitAsync() + { + unsubscribeUpdateBackLink = PubSubService.Subscribe(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, async payload => + { + backLinkPayload = (string?)payload; + + await InvokeAsync(StateHasChanged); + }); + + if (CultureInfoManager.MultilingualEnabled) + { + cultures = CultureInfoManager.SupportedCultures + .Select(sc => new BitDropdownItem<string> { Value = sc.Culture.Name, Text = sc.DisplayName }) + .ToArray(); + } + + await base.OnInitAsync(); + } + + + private async Task HandleBackLinkClick() + { + PubSubService.Publish(ClientPubSubMessages.IDENTITY_HEADER_BACK_LINK_CLICKED, backLinkPayload); + } + + private async Task ToggleTheme() + { + await themeService.ToggleTheme(); + } + + private async Task OnCultureChanged(string? cultureName) + { + await cultureService.ChangeCulture(cultureName); + } + + + public void Dispose() + { + unsubscribeUpdateBackLink?.Invoke(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.scss new file mode 100644 index 0000000000..fea1898c1f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/IdentityHeader.razor.scss @@ -0,0 +1,22 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +header { + width: 100%; + height: 65px; + padding: 1rem; + position: absolute; + z-index: $bit-zindex-base; +} + +::deep { + .language-callout { + @include lt-sm { + height: unset; + max-width: 225px; + box-shadow: none; + bottom: var(--app-inset-bottom); + top: var(--app-inset-top) !important; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor new file mode 100644 index 0000000000..8b80dac1b0 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor @@ -0,0 +1,4 @@ +@inherits AppComponentBase + +@* this component creates a bridge between the js code (app.ts) and the .net code + that can easily be used to call any .net code from js without any hassle. *@ \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor.cs new file mode 100644 index 0000000000..4acf8c6e2e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/JsBridge.razor.cs @@ -0,0 +1,34 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class JsBridge : IDisposable +{ + private DotNetObjectReference<JsBridge>? dotnetObj; + /// <summary> + /// at the rendering time of this component (the component is added to the `RootLayout`) + /// it registers an instance of the `DotNetObjectReference` into the js code (look at the `app.ts` file), + /// so we can later use it to call .net methods. + /// </summary> + protected override async Task OnAfterFirstRenderAsync() + { + dotnetObj = DotNetObjectReference.Create(this); + + await JSRuntime.InvokeVoidAsync("App.registerJsBridge", dotnetObj); + + await base.OnAfterFirstRenderAsync(); + } + + + /// <summary> + /// you can add any other method like this to utilize the bridge between js and .net code. + /// </summary> + [JSInvokable(nameof(ShowDiagnostic))] + public async Task ShowDiagnostic() + { + PubSubService.Publish(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL); + } + + public void Dispose() + { + dotnetObj?.Dispose(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor deleted file mode 100644 index bb7abc679c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor +++ /dev/null @@ -1,25 +0,0 @@ -@inherits LayoutComponentBase - -<div class="status-bar">Boilerplate</div> - -<CascadingValue Value="currentDir"> - <div dir="@currentDir?.ToString()" class="layout"> - <Header OnToggleMenu=ToggleMenuHandler /> - - <main> - @if (isUserAuthenticated) - { - <NavMenu @bind-IsMenuOpen=isMenuOpen /> - } - <div class="main-content"> - <AppErrorBoundary> - @Body - </AppErrorBoundary> - </div> - </main> - - <Footer /> - - <MessageBox /> - </div> -</CascadingValue> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.cs deleted file mode 100644 index 8f3c081f46..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.AspNetCore.Components.Web; - -namespace Boilerplate.Client.Core.Components.Layout; - -public partial class MainLayout : IDisposable -{ - private bool disposed; - private bool isMenuOpen; - private BitDir? currentDir; - private bool isUserAuthenticated; - private ErrorBoundary errorBoundaryRef = default!; - private Action unsubscribeCultureChange = default!; - - [AutoInject] private IPubSubService pubSubService = default!; - [AutoInject] private AuthenticationManager authManager = default!; - [AutoInject] private IExceptionHandler exceptionHandler = default!; - [AutoInject] private IPrerenderStateService prerenderStateService = default!; - - [CascadingParameter] public Task<AuthenticationState> AuthenticationStateTask { get; set; } = default!; - - protected override async Task OnInitializedAsync() - { - try - { - authManager.AuthenticationStateChanged += AuthenticationStateChanged; - - isUserAuthenticated = await prerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.IsAuthenticated()); - - unsubscribeCultureChange = pubSubService.Subscribe(PubSubMessages.CULTURE_CHANGED, async _ => - { - SetCurrentDir(); - - StateHasChanged(); - }); - - SetCurrentDir(); - - await base.OnInitializedAsync(); - } - catch (Exception exp) - { - exceptionHandler.Handle(exp); - } - } - - protected override void OnParametersSet() - { - // TODO: we can try to recover from exception after rendering the ErrorBoundary with this line. - // but for now it's better to persist the error ui until a force refresh. - // ErrorBoundaryRef.Recover(); - - base.OnParametersSet(); - } - - private void SetCurrentDir() - { - var currentCulture = CultureInfo.CurrentUICulture; - - currentDir = currentCulture.TextInfo.IsRightToLeft ? BitDir.Rtl : null; - } - - private void ToggleMenuHandler() - { - isMenuOpen = !isMenuOpen; - } - - private async void AuthenticationStateChanged(Task<AuthenticationState> task) - { - try - { - isUserAuthenticated = (await task).User.IsAuthenticated(); - } - catch (Exception ex) - { - exceptionHandler.Handle(ex); - } - finally - { - await InvokeAsync(StateHasChanged); - } - } - - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposed || disposing is false) return; - - authManager.AuthenticationStateChanged -= AuthenticationStateChanged; - - unsubscribeCultureChange?.Invoke(); - - disposed = true; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.scss deleted file mode 100644 index effcd9f6ed..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.scss +++ /dev/null @@ -1,149 +0,0 @@ -@import '../../Styles/abstracts/_vars.scss'; -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.layout { - width: 100%; - height: 100%; -} - -main { - width: 100%; - display: flex; - min-height: 100vh; - position: relative; - box-sizing: border-box; - justify-content: flex-start; - padding-bottom: $footerHeight; - background-color: $bit-color-background-primary; - padding-top: calc($headerHeight + var(--bit-status-bar-height)); - - .bit-ios & { - padding-top: calc($headerHeight + env(safe-area-inset-top)); - padding-bottom: calc($footerHeight + env(safe-area-inset-bottom)); - } -} - -.main-content { - flex-grow: 1; - display: flex; - padding: 1rem; - min-height: 100%; - flex-flow: column; - align-items: center; - justify-content: center; - width: calc(100% - $navMenuWidth); - background-color: $bit-color-background-secondary; - - @include lt-lg { - width: 100%; - } -} - -.status-bar { - top: 0; - width: 100%; - z-index: 101; - display: none; - position: fixed; - overflow: hidden; - text-align: center; - align-items: center; - font-size: rem2(14px); - justify-content: center; - background-color: transparent; - - .bit-ios & { - display: flex; - color: transparent; - height: env(safe-area-inset-top); - } - - .bit-windows &, .bit-macos & { - display: flex; - height: var(--bit-status-bar-height); - } -} - -::deep { - .form { - width: 100%; - display: flex; - padding: 2rem; - position: relative; - text-align: center; - align-items: center; - max-width: rem2(450px); - justify-content: center; - flex-flow: column nowrap; - border-radius: rem2(4px); - box-shadow: $bit-box-shadow-callout; - background-color: $bit-color-background-primary; - } - - .form-title { - font-weight: 600; - font-size: rem2(28px); - line-height: rem2(44px); - margin-bottom: rem2(25px); - - @include lg { - font-size: rem2(24px); - line-height: rem2(36px); - } - - @include md { - font-size: rem2(22px); - line-height: rem2(36px); - } - - @include sm { - font-size: rem2(20px); - line-height: rem2(32px); - } - } - - .form-input-container { - width: 100%; - display: flex; - text-align: start; - max-width: rem2(300px); - flex-flow: column nowrap; - margin-bottom: rem2(21px); - } - - .form-input-error, - .validation-message { - text-align: start; - font-size: rem2(12px); - line-height: rem2(16px); - color: $bit-color-error; - } - - .form-submit-button { - width: 100%; - max-width: rem2(300px); - margin-bottom: rem2(10px); - } - - .form-message-bar { - width: 100%; - position: absolute; - text-align: center; - inset-block-start: 0; - inset-inline-start: 0; - border-radius: rem2(4px) rem2(4px) 0 0; - scroll-margin-top: calc($headerHeight + var(--bit-status-bar-height) + 1px); - } -} - -.bit-ios { - ::deep .form-message-bar { - scroll-margin-top: calc($headerHeight + env(safe-area-inset-top) + 1px); - } -} - -::deep .loading-container { - text-align: center; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor index fe51a4bde7..6abaf8a1bb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor @@ -1,16 +1,32 @@ @inherits AppComponentBase -<BitModal @bind-IsOpen="isOpen" IsBlocking="true" AutoToggleScroll="false"> - <div class="main"> - <div class="header"> - <span class="title">@title</span> - <BitButton IconName="@BitIconName.ChromeClose" OnClick="OnCloseClick" Variant="BitVariant.Text" /> - </div> - <div class="body"> - @body - </div> - <div class="footer"> - <BitButton OnClick="OnOkClick">OK</BitButton> - </div> - </div> -</BitModal> +<div> + <BitModal @bind-IsOpen="isOpen" Blocking Class="root" Classes="@(new() { Content = "content" })"> + <section> + <BitStack Class="stack"> + <BitStack Horizontal AutoHeight> + <BitText Typography="BitTypography.H5" Color="BitColor.Tertiary"> + @title + </BitText> + + <BitSpacer /> + + <BitButton OnClick="OnCloseClick" + Color="BitColor.Tertiary" + Variant="BitVariant.Text" + IconName="@BitIconName.ChromeClose" /> + </BitStack> + + <BitText Color="BitColor.Tertiary" Class="body"> + @body + </BitText> + + <BitStack Alignment="BitAlignment.Center" AutoHeight> + <BitButton OnClick="OnOkClick" Color="BitColor.Tertiary"> + @Localizer[AppStrings.Ok] + </BitButton> + </BitStack> + </BitStack> + </section> + </BitModal> +</div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.cs index 42ff6c5004..6b38366c85 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.cs @@ -12,7 +12,7 @@ public partial class MessageBox protected override Task OnInitAsync() { - unsubscribe = PubSubService.Subscribe(PubSubMessages.SHOW_MESSAGE, async args => + unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SHOW_MESSAGE, async args => { var data = (MessageBoxData)args!; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.scss index 6077b51ee0..663287364a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MessageBox.razor.scss @@ -1,31 +1,41 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_media-queries.scss'; -.main { - flex-grow: 1; - display: flex; - padding: rem2(10px); - flex-direction: column; -} +section { + padding: 1rem; + min-width: 20rem; + max-height: var(--app-height); -.header { - display: flex; + @include lt-md { + min-width: unset; + } } -.title { - flex-grow: 1; -} +::deep { + .root { + width: var(--app-width); + height: var(--app-height); + top: var(--app-inset-top); + left: var(--app-inset-left); + right: var(--app-inset-right); + bottom: var(--app-inset-bottom); + } -.body { - flex-grow: 1; - display: flex; - overflow: auto; - max-width: 90vw; - white-space: pre; - margin: rem2(10px) 0; -} + .content { + @include lt-md { + min-width: 95%; + max-width: 95%; + } + } + + .stack { + max-height: calc(var(--app-height) - 3rem); + } -.footer { - display: flex; - justify-content: center; + .body { + width: 100%; + flex-grow: 1; + display: flex; + overflow: auto; + white-space: pre; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor new file mode 100644 index 0000000000..bb680883ea --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor @@ -0,0 +1,50 @@ +@*+:cnd:noEmit*@ +@inherits AppComponentBase; + +<section> + <a href="@Urls.HomePage"> + <BitIcon IconName="@BitIconName.Home" Color="@(IsActive(Urls.HomePage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.HomePage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Home)] + </BitText> + </a> + + @*#if (sample == "Todo")*@ + <a href="@Urls.TodoPage"> + <BitIcon IconName="@BitIconName.ToDoLogoOutline" Color="@(IsActive(Urls.TodoPage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.TodoPage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Todo)] + </BitText> + </a> + @*#endif*@ + + @*#if (sample == "Admin")*@ + <a href="@Urls.DashboardPage"> + <BitIcon IconName="@BitIconName.BarChartVerticalFill" Color="@(IsActive(Urls.DashboardPage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.DashboardPage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Dashboard)] + </BitText> + </a> + + <a href="@Urls.ProductsPage"> + <BitIcon IconName="@BitIconName.Product" Color="@(IsActive(Urls.ProductsPage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.ProductsPage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Products)] + </BitText> + </a> + + <a href="@Urls.CategoriesPage"> + <BitIcon IconName="@BitIconName.BuildQueue" Color="@(IsActive(Urls.CategoriesPage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.CategoriesPage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Categories)] + </BitText> + </a> + @*#endif*@ + + <a href="@Urls.TermsPage"> + <BitIcon IconName="@BitIconName.EntityExtraction" Color="@(IsActive(Urls.TermsPage) ? BitColor.Primary : BitColor.SecondaryForeground)" /> + <BitText Typography="BitTypography.Caption2" Color="@(IsActive(Urls.TermsPage) ? BitColor.Primary : BitColor.SecondaryForeground)"> + @Localizer[nameof(AppStrings.Terms)] + </BitText> + </a> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.cs new file mode 100644 index 0000000000..c9d1de54e6 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.cs @@ -0,0 +1,11 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class NavBar +{ + [CascadingParameter(Name = Parameters.CurrentUrl)] private string? currentUrl { get; set; } + + private bool IsActive(string url) + { + return currentUrl == url; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.scss new file mode 100644 index 0000000000..b38dfc96a2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor.scss @@ -0,0 +1,27 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +section { + width: 100%; + display: flex; + height: 3.5rem; + position: fixed; + align-items: center; + max-width: var(--app-width); + justify-content: space-around; + bottom: var(--app-inset-bottom); + background-color: $bit-color-background-primary; + border-top: 1px solid $bit-color-border-tertiary; + + @include gt-sm { + display: none; + } +} + +a { + gap: 0.25rem; + display: flex; + align-items: center; + text-decoration: none; + flex-direction: column; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor deleted file mode 100644 index 005e59b074..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor +++ /dev/null @@ -1,35 +0,0 @@ -@inherits AppComponentBase; - -@if (IsMenuOpen) -{ - <div class="menu-overlay" @onclick=CloseMenu></div> -} - -<section class="main-container @(IsMenuOpen ? null : "main-container--closed")"> - <div class="top-container"> - @{ - var imageUrl = user.ProfileImageName is null ? null : $"{profileImageUrl}&file={user.ProfileImageName}"; - } - <BitPersona Class="persona" - PrimaryText="@user.FullName" - SecondaryText="@user.DisplayName" - Size=@BitPersonaSize.Size48 - Presence="BitPersonaPresence.Online" - ImageUrl="@imageUrl" - OnImageClick=GoToProfile> - <ImageOverlayTemplate> - <span>@Localizer[nameof(AppStrings.Edit)]</span> - </ImageOverlayTemplate> - </BitPersona> - <br /> - <BitActionButton IconName="@BitIconName.SignOut" OnClick=DoSignOut Style="width:fit-content"> - @Localizer[nameof(AppStrings.SignOut)] - </BitActionButton> - </div> - <BitNav Items="navItems" - DefaultSelectedItem="navItems[0]" - OnItemClick="(BitNavItem item) => HandleNavItemClick(item)"> - </BitNav> -</section> - -<SignOutConfirmModal @bind-IsOpen=isSignOutModalOpen></SignOutConfirmModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.cs deleted file mode 100644 index b2b6fc8dc9..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.cs +++ /dev/null @@ -1,149 +0,0 @@ -//+:cnd:noEmit -using Boilerplate.Shared.Dtos.Identity; - -namespace Boilerplate.Client.Core.Components.Layout; - -public partial class NavMenu -{ - private bool disposed; - private bool isSignOutModalOpen; - private string? profileImageUrl; - private UserDto user = new(); - private List<BitNavItem> navItems = []; - private Action unsubscribe = default!; - - [AutoInject] private NavigationManager navManager = default!; - - [Parameter] public bool IsMenuOpen { get; set; } - - [Parameter] public EventCallback<bool> IsMenuOpenChanged { get; set; } - - protected override async Task OnInitAsync() - { - navItems = - [ - new() - { - Text = Localizer[nameof(AppStrings.Home)], - IconName = BitIconName.Home, - Url = Urls.HomePage, - }, - //#if (sample == "Admin") - new() - { - Text = Localizer[nameof(AppStrings.ProductCategory)], - IconName = BitIconName.Product, - IsExpanded = true, - ChildItems = - [ - new() { - Text = Localizer[nameof(AppStrings.Dashboard)], - Url = Urls.DashboardPage, - }, - new() { - Text = Localizer[nameof(AppStrings.Products)], - Url = Urls.ProductsPage, - }, - new() { - Text = Localizer[nameof(AppStrings.Categories)], - Url = Urls.CategoriesPage, - }, - ] - }, - //#elif (sample == "Todo") - new() - { - Text = Localizer[nameof(AppStrings.TodoTitle)], - IconName = BitIconName.ToDoLogoOutline, - Url = Urls.TodoPage, - }, - //#endif - new() - { - Text = Localizer[nameof(AppStrings.ProfileTitle)], - IconName = BitIconName.EditContact, - Url = Urls.ProfilePage, - }, - //#if (offlineDb == true) - new() - { - Text = Localizer[nameof(AppStrings.OfflineEditProfileTitle)], - IconName = BitIconName.EditContact, - Url = Urls.OfflineEditProfilePage, - }, - //#endif - new() - { - Text = Localizer[nameof(AppStrings.TermsTitle)], - IconName = BitIconName.EntityExtraction, - Url = Urls.TermsPage, - } - ]; - - if (AppPlatform.IsBlazorHybrid) - { - // Presently, the About page is absent from the Client/Core project, rendering it inaccessible on the web platform. - // In order to exhibit a sample page that grants direct access to native functionalities without dependence on dependency injection (DI) or publish-subscribe patterns, - // about page is integrated within Blazor hybrid projects like Client/Maui. - - navItems.Add(new() - { - Text = Localizer[nameof(AppStrings.AboutTitle)], - IconName = BitIconName.HelpMirrored, - Url = Urls.AboutPage, - }); - } - - unsubscribe = PubSubService.Subscribe(PubSubMessages.USER_DATA_UPDATED, async payload => - { - if (payload is null) return; - - user = (UserDto)payload; - - await InvokeAsync(StateHasChanged); - }); - - user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", AppJsonContext.Default.UserDto, CurrentCancellationToken)))!; - - var serverAddress = Configuration.GetServerAddress(); - var access_token = await PrerenderStateService.GetValue(() => AuthTokenProvider.GetAccessTokenAsync()); - profileImageUrl = $"{serverAddress}/api/Attachment/GetProfileImage?access_token={access_token}"; - } - - private async Task DoSignOut() - { - isSignOutModalOpen = true; - - await CloseMenu(); - } - - private async Task GoToProfile() - { - await CloseMenu(); - navManager.NavigateTo(Urls.ProfilePage); - } - - private async Task HandleNavItemClick(BitNavItem item) - { - if (string.IsNullOrEmpty(item.Url)) return; - - await CloseMenu(); - } - - private async Task CloseMenu() - { - IsMenuOpen = false; - await IsMenuOpenChanged.InvokeAsync(false); - } - - protected override async ValueTask DisposeAsync(bool disposing) - { - await base.DisposeAsync(disposing); - - if (disposed || disposing is false) return; - - unsubscribe?.Invoke(); - - disposed = true; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.scss deleted file mode 100644 index d19f851b56..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavMenu.razor.scss +++ /dev/null @@ -1,81 +0,0 @@ -@import '../../Styles/abstracts/_vars.scss'; -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.main-container { - height: 100%; - display: flex; - position: sticky; - min-height: 100%; - max-height: 100vh; - overflow: hidden auto; - flex-flow: column nowrap; - justify-content: flex-start; - min-width: rem2($navMenuWidth); - background-color: $bit-color-background-primary; - top: calc($headerHeight + var(--bit-status-bar-height)); - padding-bottom: calc($headerHeight + $footerHeight + var(--bit-status-bar-height)); - - .bit-ios & { - top: calc($headerHeight + env(safe-area-inset-top)); - } - - &::-webkit-scrollbar { - width: 0; - } -} - -@include lt-lg { - .main-container { - z-index: 3; - position: fixed; - padding-bottom: 0; - top: calc(var(--bit-status-bar-height)); - - .bit-ios & { - top: 0; - padding-top: calc(env(safe-area-inset-top)); - } - } - - .main-container--closed { - display: none; - } -} - -.top-container { - width: 100%; - display: flex; - padding: rem2(16px); - flex-flow: column nowrap; - margin-bottom: rem2(20px); - justify-content: flex-start; - border-bottom: rem2(1px) solid $bit-color-border-secondary; -} - -.menu-overlay { - top: 0; - z-index: 2; - width: 100%; - height: 100%; - position: fixed; - min-height: 100vh; - inset-inline-start: 0; - background-color: rgba(0, 0, 0, 0.5); - - @include gt-md { - display: none; - } -} - -::deep { - a { - text-decoration: none; - } - - .persona { - margin: auto; - max-width: rem2(calc($navMenuWidth - 20px)); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor new file mode 100644 index 0000000000..62c6a8e087 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor @@ -0,0 +1,76 @@ +@inherits AppComponentBase; + +@if (isMenuOpen) +{ + <div class="menu-overlay" @onclick=CloseMenu></div> +} + +@{ + var isToggled = !isMenuOpen && isMenuToggled; +} + +<section class="@(isMenuOpen ? "" : "closed")" Style="@($"--nav-menu-width:{(isToggled ? "6rem" : "14rem")}")"> + <BitCard Class="panel"> + <BitStack HorizontalAlign="@(isToggled ? BitAlignment.Center : BitAlignment.Start)" Grows> + <BitStack Horizontal AutoHeight Alignment="BitAlignment.Center"> + <BitImage Src="_content/Boilerplate.Client.Core/images/bit-logo.svg" + Visibility="@(isToggled ? BitVisibility.Collapsed : BitVisibility.Visible)" /> + <BitSpacer Visibility="@(isToggled ? BitVisibility.Collapsed : BitVisibility.Visible)" /> + <BitButton IconOnly FixedColor + Class="toggle-btn" + Size="BitSize.Large" + OnClick="ToggleNavPanel" + Variant="BitVariant.Text" + Color="BitColor.TertiaryBackground" + IconName="@BitIconName.ColumnRightTwoThirds" /> + </BitStack> + + <BitSearchBox @ref="searchBoxRef" + Underlined Immediate DebounceTime="500" + OnChange="WrapHandled<string?>(SearchNavItems)" + Style="@(isToggled ? "display:none" : "")" + Styles="@(new() { Root="width:100%", InputContainer="width:100%" })" /> + + @if (isToggled) + { + <BitButton IconOnly FixedColor + Class="toggle-btn" + Size="BitSize.Large" + OnClick="ToggleForSearch" + Variant="BitVariant.Text" + IconName="@BitIconName.Search" + Color="BitColor.TertiaryBackground" /> + } + + @if (filteredNavItems.Count == 0) + { + if (isToggled is false) + { + <BitText>@Localizer[nameof(AppStrings.NothingFound)]</BitText> + } + } + else + { + <BitNav FullWidth + IconOnly="isToggled" + Items="filteredNavItems" + Accent="BitColor.SecondaryBackground" + DefaultSelectedItem="filteredNavItems[0]" + OnItemClick="(BitNavItem item) => HandleNavItemClick(item)" + Styles="@(new() { SelectedItemContainer = "background-color: var(--bit-clr-bg-sec-active)" })" /> + } + + <BitSpacer /> + + <BitActionButton OnClick="DoSignOut" + IconOnly="isToggled" + FullWidth="!isToggled" + IconName="@BitIconName.SignOut" + Title="@Localizer[nameof(AppStrings.SignOut)]"> + @(isToggled ? "" : Localizer[nameof(AppStrings.SignOut)]) + </BitActionButton> + </BitStack> + </BitCard> +</section> + +<SignOutConfirmDialog @bind-IsOpen=isSignOutModalOpen></SignOutConfirmDialog> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.cs new file mode 100644 index 0000000000..8f385e52e8 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.cs @@ -0,0 +1,103 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class NavPanel +{ + private bool disposed; + private bool isMenuOpen; + private bool isMenuToggled; + private bool isSignOutModalOpen; + private List<BitNavItem> allNavItems = []; + private Action unsubOpenNavPanel = default!; + private BitSearchBox searchBoxRef = default!; + private Action unsubUserDataChange = default!; + private List<BitNavItem> flatNavItemList = []; + private List<BitNavItem> filteredNavItems = []; + + + protected override async Task OnInitAsync() + { + CreateNavItems(); + flatNavItemList = Flatten(allNavItems).ToList().FindAll(link => !string.IsNullOrEmpty(link.Url)); + + SearchNavItems(null); + + unsubOpenNavPanel = PubSubService.Subscribe(ClientPubSubMessages.OPEN_NAV_PANEL, async _ => + { + isMenuOpen = true; + StateHasChanged(); + }); + } + + + private async Task DoSignOut() + { + isSignOutModalOpen = true; + await CloseMenu(); + } + + private async Task HandleNavItemClick(BitNavItem item) + { + if (string.IsNullOrEmpty(item.Url)) return; + + filteredNavItems = allNavItems; + + await CloseMenu(); + } + + private async Task CloseMenu() + { + isMenuOpen = false; + } + + private async Task ToggleNavPanel() + { + isMenuToggled = !isMenuToggled; + if (isMenuToggled) + { + SearchNavItems(null); + } + } + + private async Task ToggleForSearch() + { + isMenuToggled = false; + await Task.Delay(1); + await searchBoxRef.FocusAsync(); + } + + private void SearchNavItems(string? searchText) + { + filteredNavItems = allNavItems; + if (searchText is null) return; + + filteredNavItems = allNavItems; + if (string.IsNullOrEmpty(searchText)) return; + + var mainItems = flatNavItemList + .FindAll(item => searchText.Split(' ') + .Where(t => string.IsNullOrEmpty(t) is false) + .Any(t => $"{item.Text} {item.Description}".Contains(t, StringComparison.InvariantCultureIgnoreCase))); + + var subItems = flatNavItemList + .FindAll(item => searchText.Split(' ') + .Where(t => string.IsNullOrEmpty(t) is false) + .Any(t => item.Data?.ToString()?.Contains(t, StringComparison.InvariantCultureIgnoreCase) ?? false)); + + filteredNavItems = [.. mainItems, .. subItems]; + } + + private static IEnumerable<BitNavItem> Flatten(IEnumerable<BitNavItem> e) => e.SelectMany(c => Flatten(c.ChildItems)).Concat(e); + + + protected override async ValueTask DisposeAsync(bool disposing) + { + await base.DisposeAsync(disposing); + + if (disposed || disposing is false) return; + + unsubOpenNavPanel?.Invoke(); + unsubUserDataChange?.Invoke(); + + disposed = true; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.items.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.items.cs new file mode 100644 index 0000000000..ef671012e4 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.items.cs @@ -0,0 +1,92 @@ +//+:cnd:noEmit +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class NavPanel +{ + private void CreateNavItems() + { + allNavItems = + [ + new() + { + Text = Localizer[nameof(AppStrings.Home)], + IconName = BitIconName.Home, + Url = Urls.HomePage, + }, + //#if (sample == "Admin") + new() + { + Text = Localizer[nameof(AppStrings.AdminPanel)], + IconName = BitIconName.Admin, + ChildItems = + [ + new() { + Text = Localizer[nameof(AppStrings.Dashboard)], + IconName = BitIconName.BarChartVerticalFill, + Url = Urls.DashboardPage, + }, + new() { + Text = Localizer[nameof(AppStrings.Categories)], + IconName = BitIconName.BuildQueue, + Url = Urls.CategoriesPage, + }, + new() { + Text = Localizer[nameof(AppStrings.Products)], + IconName = BitIconName.Product, + Url = Urls.ProductsPage, + } + ] + }, + //#elif (sample == "Todo") + new() + { + Text = Localizer[nameof(AppStrings.Todo)], + IconName = BitIconName.ToDoLogoOutline, + Url = Urls.TodoPage, + }, + //#endif + new() + { + Text = Localizer[nameof(AppStrings.Settings)], + IconName = BitIconName.Equalizer, + Url = Urls.SettingsPage, + AdditionalUrls = + [ + $"{Urls.SettingsPage}/{Urls.SettingsSections.Profile}", + $"{Urls.SettingsPage}/{Urls.SettingsSections.Account}", + $"{Urls.SettingsPage}/{Urls.SettingsSections.Tfa}", + $"{Urls.SettingsPage}/{Urls.SettingsSections.Sessions}", + ] + }, + //#if (offlineDb == true) + new() + { + Text = Localizer[nameof(AppStrings.OfflineEditProfileTitle)], + IconName = BitIconName.EditContact, + Url = Urls.OfflineEditProfilePage, + }, + //#endif + new() + { + Text = Localizer[nameof(AppStrings.Terms)], + IconName = BitIconName.EntityExtraction, + Url = Urls.TermsPage, + } + ]; + + if (AppPlatform.IsBlazorHybrid) + { + // Currently, the "About" page is absent from the Client/Core project, rendering it inaccessible on the web platform. + // In order to exhibit a sample page that grants direct access to native functionalities without dependence on + // dependency injection (DI) or publish-subscribe patterns, the "About" page is integrated within Blazor + // hybrid projects like Client/Maui. + + allNavItems.Add(new() + { + Text = Localizer[nameof(AppStrings.AboutTitle)], + IconName = BitIconName.Info, + Url = Urls.AboutPage, + }); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.scss new file mode 100644 index 0000000000..07b411db2e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavPanel.razor.scss @@ -0,0 +1,78 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +section { + top: 0; + padding: 1rem; + position: sticky; + overflow: hidden auto; + height: var(--app-height); + min-width: var(--nav-menu-width); + max-width: var(--nav-menu-width); + + &::-webkit-scrollbar { + width: 0; + } + + @include lt-md { + z-index: 3; + padding: 0; + position: fixed; + height: unset !important; + top: var(--app-inset-top); + bottom: var(--app-inset-bottom); + + &.closed { + display: none; + } + } +} + +.bit-macos { + section { + height: -webkit-fill-available; + + ::deep .panel { + height: -webkit-fill-available; + } + } +} + +.menu-overlay { + inset: 0; + z-index: 2; + width: 100%; + height: 100%; + position: fixed; + min-height: 100vh; + + @include gt-sm { + display: none; + } +} + +::deep { + .panel { + width: auto; + display: flex; + padding: 0.5rem; + min-height: 100%; + height: fit-content; + flex-direction: column; + background-color: $bit-color-background-secondary; + + @include lt-md { + padding: 1rem; + } + } + + .toggle-btn { + @include lt-md { + display: none; + } + } + + a { + text-decoration: none; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor new file mode 100644 index 0000000000..d07f46f0ff --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor @@ -0,0 +1,27 @@ +<CascadingValue Value="CurrentDir"> + <CascadingValue Value="CurrentTheme" Name="@Parameters.CurrentTheme"> + <CascadingValue Value="CurrentUrl" Name="@Parameters.CurrentUrl"> + <CascadingValue Value="CurrentRouteData" Name="@Parameters.CurrentRouteData"> + <CascadingValue Value="IsAuthenticated" Name="@Parameters.IsAuthenticated"> + <CascadingValue Value="IsCrossLayoutPage" Name="@Parameters.IsCrossLayoutPage"> + <CascadingValue Value="IsOnline" Name="@Parameters.IsOnline"> + <div class="layout"> + <div class="inset-top"></div> + <div class="inset-center" dir="@CurrentDir?.ToString().ToLower()"> + <div class="inset-left"></div> + <div class="inset-main" id="bit-inset-main"> + <AppErrorBoundary> + @ChildContent + </AppErrorBoundary> + </div> + <div class="inset-right"></div> + </div> + <div class="inset-bottom"></div> + </div> + </CascadingValue> + </CascadingValue> + </CascadingValue> + </CascadingValue> + </CascadingValue> + </CascadingValue> +</CascadingValue> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.cs new file mode 100644 index 0000000000..b4fffa2398 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.cs @@ -0,0 +1,20 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class RootContainer +{ + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// <summary> + /// <inheritdoc cref="Parameters.IsOnline"/> + /// </summary> + [Parameter] public bool? IsOnline { get; set; } + [Parameter] public BitDir? CurrentDir { get; set; } + [Parameter] public string? CurrentUrl { get; set; } + [Parameter] public bool? IsAuthenticated { get; set; } + /// <summary> + /// <inheritdoc cref="Parameters.IsCrossLayoutPage"/> + /// </summary> + [Parameter] public bool? IsCrossLayoutPage { get; set; } + [Parameter] public AppThemeType? CurrentTheme { get; set; } + [Parameter] public RouteData? CurrentRouteData { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.scss new file mode 100644 index 0000000000..64d032ac15 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootContainer.razor.scss @@ -0,0 +1,54 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +.layout { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background-color: $bit-color-background-primary; +} + +.inset-top { + width: 100%; + z-index: 999999; + height: var(--app-inset-top); + background-color: $bit-color-background-primary; +} + +.inset-bottom { + width: 100%; + z-index: 999999; + height: var(--app-inset-bottom); + background-color: $bit-color-background-primary; +} + +.inset-center { + width: 100%; + display: flex; + height: calc(100% - var(--app-inset-top) - var(--app-inset-bottom)); +} + +.inset-main { + height: 100%; + display: flex; + overflow: auto; + position: relative; + scroll-behavior: smooth; + overscroll-behavior: none; + width: calc(100% - var(--app-inset-left) - var(--app-inset-right)); +} + +.inset-left { + height: 100%; + z-index: 999999; + width: var(--app-inset-left); + background-color: $bit-color-background-primary; +} + +.inset-right { + height: 100%; + z-index: 999999; + width: var(--app-inset-right); + background-color: $bit-color-background-primary; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor new file mode 100644 index 0000000000..7b0a8a96eb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor @@ -0,0 +1,51 @@ +@inherits LayoutComponentBase + +<RootContainer IsOnline="isOnline" + CurrentDir="currentDir" + CurrentUrl="@currentUrl" + CurrentTheme="currentTheme" + IsAuthenticated="isAuthenticated" + CurrentRouteData="currentRouteData" + IsCrossLayoutPage="isCrossLayoutPage"> + <main class="@GetMainCssClass()"> + <div class="root"> + @if (isAuthenticated is false) + { + <IdentityHeader /> + } + else if (isAuthenticated is true) + { + <NavPanel /> + } + + <div class="stack"> + @if (isAuthenticated is true) + { + <AuthorizedHeader /> + } + + <div class="body"> + @Body + </div> + + @if (isAuthenticated is false && isCrossLayoutPage is false) + { + <div class="panel"> + <BitImage Width="70%" Src="_content/Boilerplate.Client.Core/images/identitylayout-image.webp" /> + </div> + } + </div> + </div> + + @if (isAuthenticated is true) + { + <NavBar /> + } + </main> +</RootContainer> + +<SnackBar /> +<MessageBox /> +<DiagnosticModal /> + +<JsBridge /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs new file mode 100644 index 0000000000..44cf85bfc7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs @@ -0,0 +1,199 @@ +//+:cnd:noEmit +using System.Reflection; + +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class RootLayout : IDisposable +{ + private BitDir? currentDir; + private string? currentUrl; + + /// <summary> + /// <inheritdoc cref="Parameters.IsOnline"/> + /// </summary> + private bool? isOnline = null; + private bool? isAuthenticated; + + /// <summary> + /// <inheritdoc cref="Parameters.IsCrossLayoutPage"/> + /// </summary> + private bool? isCrossLayoutPage; + private AppThemeType? currentTheme; + private RouteData? currentRouteData; + private List<Action> unsubscribers = []; + + + [AutoInject] private Keyboard keyboard = default!; + [AutoInject] private ThemeService themeService = default!; + [AutoInject] private PubSubService pubSubService = default!; + [AutoInject] private AuthenticationManager authManager = default!; + [AutoInject] private IExceptionHandler exceptionHandler = default!; + [AutoInject] private ITelemetryContext telemetryContext = default!; + [AutoInject] private NavigationManager navigationManager = default!; + [AutoInject] private IPrerenderStateService prerenderStateService = default!; + + + [CascadingParameter] public Task<AuthenticationState> AuthenticationStateTask { get; set; } = default!; + + + protected override async Task OnInitializedAsync() + { + try + { + navigationManager.LocationChanged += NavigationManagerLocationChanged; + authManager.AuthenticationStateChanged += AuthenticationStateChanged; + unsubscribers.Add(pubSubService.Subscribe(ClientPubSubMessages.CULTURE_CHANGED, async _ => + { + SetCurrentDir(); + StateHasChanged(); + })); + unsubscribers.Add(pubSubService.Subscribe(ClientPubSubMessages.THEME_CHANGED, async payload => + { + if (payload is null) return; + currentTheme = (AppThemeType)payload; + StateHasChanged(); + })); + unsubscribers.Add(pubSubService.Subscribe(ClientPubSubMessages.ROUTE_DATA_UPDATED, async payload => + { + currentRouteData = (RouteData?)payload; + SetIsCrossLayout(); + StateHasChanged(); + })); + + unsubscribers.Add(pubSubService.Subscribe(ClientPubSubMessages.IS_ONLINE_CHANGED, async payload => + { + isOnline = (bool)payload!; + telemetryContext.IsOnline = isOnline is true; + await InvokeAsync(StateHasChanged); + })); + + isAuthenticated = await prerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.IsAuthenticated()); + + SetCurrentDir(); + SetCurrentUrl(); + currentTheme = await themeService.GetCurrentTheme(); + + await base.OnInitializedAsync(); + } + catch (Exception exp) + { + exceptionHandler.Handle(exp); + } + } + + protected override void OnParametersSet() + { + // TODO: we can try to recover from exception after rendering the ErrorBoundary with this line. + // but for now it's better to persist the error ui until a force refresh. + // ErrorBoundaryRef.Recover(); + + base.OnParametersSet(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await keyboard.Add(ButilKeyCodes.KeyX, OpenDiagnosticModal, ButilModifiers.Ctrl | ButilModifiers.Shift); + } + + await base.OnAfterRenderAsync(firstRender); + } + + + private async void AuthenticationStateChanged(Task<AuthenticationState> task) + { + try + { + isAuthenticated = (await task).User.IsAuthenticated(); + } + catch (Exception ex) + { + exceptionHandler.Handle(ex); + } + finally + { + await InvokeAsync(StateHasChanged); + } + } + + private void NavigationManagerLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e) + { + SetCurrentUrl(); + StateHasChanged(); + } + + + private void SetCurrentDir() + { + currentDir = CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft ? BitDir.Rtl : null; + } + + private void SetCurrentUrl() + { + var path = navigationManager.GetUriPath(); + + currentUrl = Urls.All.SingleOrDefault(pageUrl => + { + return pageUrl == Urls.HomePage + ? pageUrl == path + : path.StartsWith(pageUrl); + }); + } + + private void SetIsCrossLayout() + { + // The cross-layout pages are the pages that are getting rendered in multiple layouts (authenticated and unauthenticated). + + if (currentRouteData is null) + { + isCrossLayoutPage = true; + return; + } + + var type = currentRouteData.PageType; + + if (type.GetCustomAttributes<AuthorizeAttribute>(inherit: true).Any()) + { + isCrossLayoutPage = false; + return; + } + + if (type.Namespace?.Contains("Client.Core.Components.Pages.Identity") ?? false) + { + isCrossLayoutPage = false; + return; + } + + isCrossLayoutPage = true; + } + + private void OpenDiagnosticModal() + { + pubSubService.Publish(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL); + } + + + private string GetMainCssClass() + { + var authClass = isAuthenticated is false ? "unauthenticated" + : isAuthenticated is true ? "authenticated" + : string.Empty; + + var crossClass = isCrossLayoutPage is true ? " cross-layout" : string.Empty; + + return authClass + crossClass; + } + + + public void Dispose() + { + navigationManager.LocationChanged -= NavigationManagerLocationChanged; + + authManager.AuthenticationStateChanged -= AuthenticationStateChanged; + + unsubscribers.ForEach(d => d.Invoke()); + + _ = keyboard?.DisposeAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.scss new file mode 100644 index 0000000000..c238500a1e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.scss @@ -0,0 +1,115 @@ +@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_bit-css-variables.scss'; + +main { + width: 100%; + min-height: 100%; + + @include lt-md { + height: unset; + min-height: var(--app-height); + } + + &.unauthenticated { + .root { + width: 100%; + height: 100%; + flex-direction: column; + } + + &.cross-layout { + .body { + width: 100%; + padding: 2rem; + padding-top: 5rem; + } + } + + .stack { + width: auto; + flex-grow: 1; + flex-direction: row; + } + + .panel { + width: 70%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + @include lt-md { + display: none; + } + } + + .body { + width: 30%; + padding: 4rem; + min-width: 35rem; + padding-top: 5rem; + background-color: $bit-color-background-secondary; + border-inline-end: 1px solid $bit-color-border-secondary; + + @include lt-md { + width: 100%; + border: none; + min-width: unset; + padding-inline: 1rem; + background-color: $bit-color-background-primary; + } + } + } + + &.authenticated { + .root { + width: auto; + height: auto; + flex-direction: row; + } + + .body { + width: 100%; + height: 100%; + background-color: $bit-color-background-primary; + + @include lt-md { + height: unset; + padding-bottom: 3.5rem; + } + } + + .stack { + gap: 2rem; + flex-grow: 1; + padding: 1rem; + flex-direction: column; + + @include lt-md { + gap: 1rem; + } + + @include lt-sm { + gap: 0; + } + } + } + + .root { + display: flex; + } + + .stack { + width: 100%; + display: flex; + } +} + +::deep { + .validation-message { + font-size: 12px; + text-align: start; + line-height: 16px; + color: $bit-color-error; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor new file mode 100644 index 0000000000..e2c2fcbfca --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor @@ -0,0 +1,9 @@ +@inherits AppComponentBase + +<BitDialog @bind-IsOpen="IsOpen" + OnCancel="CloseModal" + OnOk="WrapHandled(SignOut)" + OkText="@Localizer[nameof(AppStrings.SignOut)]" + CancelText="@Localizer[nameof(AppStrings.Cancel)]" + Title="@Localizer[nameof(AppStrings.SignOut)]" + Message="@Localizer[nameof(AppStrings.SignOutPrompt)]" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.cs similarity index 54% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.cs index 97a3697a84..c276ede90b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.cs @@ -1,28 +1,15 @@ namespace Boilerplate.Client.Core.Components.Layout; -public partial class SignOutConfirmModal +public partial class SignOutConfirmDialog { - private bool isOpen; - - [Parameter] - public bool IsOpen - { - get => isOpen; - set - { - if (value == isOpen) return; - - isOpen = value; - - _ = IsOpenChanged.InvokeAsync(value); - } - } + [Parameter] public bool IsOpen { get; set; } [Parameter] public EventCallback<bool> IsOpenChanged { get; set; } private async Task CloseModal() { IsOpen = false; + await IsOpenChanged.InvokeAsync(false); } private async Task SignOut() diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.scss new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmDialog.razor.scss @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor deleted file mode 100644 index b1e8cc2e3a..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor +++ /dev/null @@ -1,19 +0,0 @@ -@inherits AppComponentBase - -<BitModal @bind-IsOpen="IsOpen" IsBlocking="true" AutoToggleScroll="false"> - <div class="modal-body"> - <div class="modal-close-btn-container"> - <BitButton OnClick="CloseModal" IconName="@BitIconName.ChromeClose" Title="Close" Variant="BitVariant.Text" /> - </div> - <div class="modal-title">@Localizer[nameof(AppStrings.SignOut)]</div> - <div class="modal-desc">@Localizer[nameof(AppStrings.SignOutPrompt)]</div> - <BitStack Horizontal> - <BitButton OnClick="SignOut" AutoLoading> - @Localizer[nameof(AppStrings.SignOut)] - </BitButton> - <BitButton OnClick="CloseModal" Variant="BitVariant.Outline"> - @Localizer[nameof(AppStrings.Cancel)] - </BitButton> - </BitStack> - </div> -</BitModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.scss deleted file mode 100644 index 36d6798ca4..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SignOutConfirmModal.razor.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; - -.modal-body { - display: flex; - position: relative; - align-items: center; - justify-content: center; - flex-flow: column nowrap; - padding: rem2(28px) rem2(110px); - - @include lt-lg { - padding: rem2(28px) rem2(47px); - } -} - -.modal-close-btn-container { - position: absolute; - inset-inline-end: 0; - inset-block-start: 0; -} - -.modal-title { - font-weight: 600; - font-size: rem2(24px); - line-height: rem2(40px); - margin-bottom: rem2(12px); -} - -.modal-desc { - font-size: rem2(18px); - line-height: rem2(24px); - margin-bottom: rem2(36px); -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor new file mode 100644 index 0000000000..8b06dcce83 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor @@ -0,0 +1,7 @@ +@inherits AppComponentBase + +<BitSnackBar @ref="snackbarRef" + Multiline + AutoDismiss + Class="snackbar" + AutoDismissTime="TimeSpan.FromSeconds(10)" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.cs new file mode 100644 index 0000000000..60609cacfd --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.cs @@ -0,0 +1,26 @@ +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class SnackBar : IDisposable +{ + private Action? unsubscribe; + private BitSnackBar snackbarRef = default!; + + + protected override Task OnInitAsync() + { + unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SHOW_SNACK, async args => + { + var (title, body, color) = (ValueTuple<string, string, BitColor>)args!; + + await snackbarRef.Show(title, body, color); + }); + + return base.OnInitAsync(); + } + + + public void Dispose() + { + unsubscribe?.Invoke(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.scss new file mode 100644 index 0000000000..2d6c5aa7f5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor.scss @@ -0,0 +1,3 @@ +.snackbar { + bottom: calc(var(--app-inset-bottom) + var(--bit-spa-scaling-factor)); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor new file mode 100644 index 0000000000..11a2957fcf --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor @@ -0,0 +1,99 @@ +@inherits AppComponentBase + +@{ + var imageUrl = user.ProfileImageName is null ? null : $"{profileImageUrl}&file={user.ProfileImageName}"; +} + +<section> + <BitDropMenu Transparent @bind-IsOpen="isOpen" Responsive + OnDismiss="() => showCultures = false" + Classes="@(new() { Callout="user-menu-callout" })"> + <Template> + <BitPersona Class="persona" + ImageUrl="@imageUrl" + Size=@BitPersonaSize.Size32 + PrimaryText="@user.DisplayName" + Classes="@(new() { DetailsContainer="persona-details" })" + Presence="@(IsOnline is null ? BitPersonaPresence.None : IsOnline is true ? BitPersonaPresence.Online : BitPersonaPresence.Offline)" /> + <BitIcon IconName="@BitIconName.ChevronDown" Size="BitSize.Small" Color="BitColor.Info" Class="menu-chevron" /> + </Template> + <Body> + <BitCard FullSize Class="user-menu-card"> + @if (showCultures is false) + { + <BitStack AutoSize> + <BitPersona Class="persona" + ImageUrl="@imageUrl" + OnImageClick="GoToProfile" + Size="BitPersonaSize.Size48" + PrimaryText="@user.DisplayName" + SecondaryText="@(user.Email ?? user.PhoneNumber)" + Presence="@(IsOnline is null ? BitPersonaPresence.None : IsOnline is true ? BitPersonaPresence.Online : BitPersonaPresence.Offline)"> + <ImageOverlayTemplate> + <span>@Localizer[nameof(AppStrings.Edit)]</span> + </ImageOverlayTemplate> + </BitPersona> + + <BitSeparator /> + + <BitActionButton IconName="@BitIconName.Globe" FullWidth OnClick="() => showCultures=true"> + <BitStack Horizontal Gap="0" Grows VerticalAlign="BitAlignment.Center"> + @Localizer[nameof(AppStrings.Language)] + <BitSpacer /> + <BitIcon Size="BitSize.Small" + Color="BitColor.SecondaryForeground" + IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChevronLeft : BitIconName.ChevronRight)" /> + </BitStack> + </BitActionButton> + + <BitStack Horizontal VerticalAlign="BitAlignment.Center" Gap="0"> + <BitActionButton Style="flex-grow:1" + OnClick="ToggleTheme" + IconName="@(currentTheme == AppThemeType.Light ? BitIconName.Sunny : BitIconName.ClearNight)"> + @(currentTheme == AppThemeType.Light ? Localizer[nameof(AppStrings.Light)] : Localizer[nameof(AppStrings.Dark)]) + </BitActionButton> + <BitToggle OnChange="ToggleTheme" Value="currentTheme == AppThemeType.Light" ValueChanged="v => {}" /> + </BitStack> + + <BitActionButton IconName="@BitIconName.Contact" Href="@($"{Urls.SettingsPage}/{Urls.SettingsSections.Profile}")" FullWidth OnClick="() => isOpen=false"> + @Localizer[nameof(AppStrings.ProfileTitle)] + </BitActionButton> + + <BitActionButton IconName="@BitIconName.SignOut" FullWidth OnClick="() => { showSignOut=true; isOpen=false; }"> + @Localizer[nameof(AppStrings.SignOut)] + </BitActionButton> + </BitStack> + } + else + { + <BitStack HorizontalAlign="BitAlignment.Start" AutoSize> + <BitActionButton FullWidth + Style="align-items:flex-end" + OnClick="() => showCultures=false" + IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBackMirrored : BitIconName.ChromeBack)"> + @Localizer[nameof(AppStrings.SelectLanguage)] + </BitActionButton> + + <BitSeparator /> + + <BitChoiceGroup Items="cultures" NoCircle Style="width:100%" + DefaultValue="@CultureInfo.CurrentUICulture.Name" + OnChange="async (string? c) => await OnCultureChanged(c)" + Styles="@(new() { ItemLabel = "flex-grow:1", ItemLabelWrapper = "width:100%" })"> + <ItemTemplate Context="item"> + <BitStack Horizontal VerticalAlign="BitAlignment.Center" Style="cursor:pointer"> + <BitImage Src="@($"_content/Boilerplate.Client.Core/images/flags/{item.Value}.webp")" /> + <BitText Typography="BitTypography.Body1" Style="@(item.IsSelected ? "font-weight:bold" : "")"> + @item.Text + </BitText> + </BitStack> + </ItemTemplate> + </BitChoiceGroup> + </BitStack> + } + </BitCard> + </Body> + </BitDropMenu> +</section> + +<SignOutConfirmDialog @bind-IsOpen="showSignOut"></SignOutConfirmDialog> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs new file mode 100644 index 0000000000..26441b6a20 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs @@ -0,0 +1,73 @@ +using Boilerplate.Shared.Dtos.Identity; + +namespace Boilerplate.Client.Core.Components.Layout; + +public partial class UserMenu +{ + private bool isOpen; + private bool showSignOut; + private bool showCultures; + private UserDto user = new(); + private string? profileImageUrl; + private Action unsubscribeUerDataUpdated = default!; + private BitChoiceGroupItem<string>[] cultures = default!; + + [AutoInject] private Cookie cookie = default!; + [AutoInject] private CultureInfoManager cultureInfoManager = default!; + [AutoInject] private ThemeService themeService { get; set; } = default!; + [AutoInject] private CultureService cultureService { get; set; } = default!; + + + [CascadingParameter] private BitDir? currentDir { get; set; } + [CascadingParameter(Name = Parameters.CurrentTheme)] private AppThemeType? currentTheme { get; set; } + + + protected override async Task OnInitAsync() + { + if (CultureInfoManager.MultilingualEnabled) + { + cultures = CultureInfoManager.SupportedCultures + .Select(sc => new BitChoiceGroupItem<string> { Value = sc.Culture.Name, Text = sc.DisplayName }) + .ToArray(); + } + + unsubscribeUerDataUpdated = PubSubService.Subscribe(ClientPubSubMessages.PROFILE_UPDATED, async payload => + { + if (payload is null) return; + + user = (UserDto)payload; + + await InvokeAsync(StateHasChanged); + }); + + user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", JsonSerializerOptions.GetTypeInfo<UserDto>(), CurrentCancellationToken)))!; + + var serverAddress = Configuration.GetServerAddress(); + var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); + profileImageUrl = $"{serverAddress}/api/Attachment/GetProfileImage?access_token={access_token}"; + + await base.OnInitAsync(); + } + + private async Task OnCultureChanged(string? cultureName) + { + await cultureService.ChangeCulture(cultureName); + } + + private async Task ToggleTheme() + { + await themeService.ToggleTheme(); + } + + private async Task GoToProfile() + { + NavigationManager.NavigateTo(Urls.SettingsPage); + } + + protected override async ValueTask DisposeAsync(bool disposing) + { + await base.DisposeAsync(disposing); + + unsubscribeUerDataUpdated?.Invoke(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.scss new file mode 100644 index 0000000000..ae7f9ec7ea --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.scss @@ -0,0 +1,32 @@ +@import '../../Styles/abstracts/_media-queries.scss'; + +::deep { + .user-menu-callout { + min-width: 225px; + + @include lt-sm { + height: unset; + box-shadow: none; + top: var(--app-inset-top) !important; + bottom: var(--app-inset-bottom) !important; + inset-inline-end: var(--app-inset-inline-start) !important; + } + } + + .user-menu-card { + @include lt-sm { + box-shadow: none; + } + } + + .persona { + max-width: 12rem; + } + + .persona-details, + .menu-chevron { + @include lt-sm { + display: none; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor similarity index 99% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor index 3d77d75ff4..22c2d90d6a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor @@ -1,15 +1,16 @@ <style> - .bit-lds-grid div { - background-color: @Color; - } - .bit-lds-wrapper { top: 50%; left: 50%; + z-index: -1; position: absolute; transform: translate(-50%, -50%); } + .bit-lds-grid div { + background-color: @Color; + } + .bit-lds-grid { width: 80px; height: 80px; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor.cs similarity index 65% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor.cs index 98279d7fbb..0b86f459bf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/LoadingComponent.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/LoadingComponent.razor.cs @@ -1,4 +1,4 @@ -namespace Boilerplate.Client.Core.Components.Layout; +namespace Boilerplate.Client.Core.Components; public partial class LoadingComponent { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/AppPageBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/AppPageBase.cs new file mode 100644 index 0000000000..3cfdab7f62 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/AppPageBase.cs @@ -0,0 +1,33 @@ + +namespace Boilerplate.Client.Core.Components.Pages; + +public abstract partial class AppPageBase : AppComponentBase +{ + protected virtual string? Title { get; } + protected virtual string? Subtitle { get; } + + [Parameter] public string? culture { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender) + { + if (string.IsNullOrEmpty(culture) is false) + { + if (CultureInfoManager.MultilingualEnabled is false || CultureInfoManager.SupportedCultures.Any(sc => string.Equals(sc.Culture.Name, culture, StringComparison.InvariantCultureIgnoreCase)) is false) + { + // Because Blazor router doesn't support regex, the '/{culture?}/' captures some irrelevant routes + // such as non existing routes like /some-invalid-url, we need to make sure that the first segment of the route is a valid culture name. + NavigationManager.NavigateTo($"{Urls.NotFoundPage}?url={NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}"); + } + } + + if (string.IsNullOrEmpty(Title) is false) + { + PubSubService.Publish(ClientPubSubMessages.PAGE_TITLE_CHANGED, (Title, Subtitle), persistent: true); + } + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor new file mode 100644 index 0000000000..879b3a31ec --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor @@ -0,0 +1,94 @@ +@attribute [Route(Urls.AddOrEditCategoryPage + "/{Id:guid?}")] +@attribute [Route("{culture?}" + Urls.AddOrEditCategoryPage + "/{Id:guid?}")] +@inherits AppPageBase + +<PageTitle> + @if (category.Id == default) + { + @Localizer[nameof(AppStrings.AddCategory)] + } + else + { + @Localizer[nameof(AppStrings.EditCategory)] + } +</PageTitle> + +<section> + <BitStack> + <BitStack Horizontal VerticalAlign="BitAlignment.Center" Gap="0.5rem"> + <BitButton Variant="BitVariant.Text" + Href="@Urls.CategoriesPage" + IconName="@BitIconName.Back" + Title="@Localizer[nameof(AppStrings.Back)]" /> + <BitText Typography="BitTypography.H5"> + @if (category.Id == default) + { + @Localizer[nameof(AppStrings.AddCategory)] + } + else + { + @Localizer[nameof(AppStrings.EditCategory)] + } + </BitText> + </BitStack> + <BitCard FullSize> + <EditForm Model="category" OnValidSubmit="WrapHandled(Save)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack Gap="0.25rem"> + @if (isLoading) + { + <BitEllipsisLoading /> + } + <BitTextField @bind-Value="category.Name" + Label="@Localizer[nameof(AppStrings.Name)]" + Placeholder="@Localizer[nameof(AppStrings.EnterCategoryName)]" /> + <ValidationMessage For="() => category.Name" /> + <br /> + <BitLabel For="catColorInput">@Localizer[nameof(AppStrings.Color)]</BitLabel> + + <BitStack> + <BitStack Horizontal> + @foreach (var color in new[] { "#FFCD56", "#FF6384", "#4BC0C0", "#FF9124", "#2B88D8", "#C7E0F4" }) + { + <button @onclick="() => SetCategoryColor(color)" + type="button" + style="background-color: @color" + class="color-btn @(category.Color == color ? "color-btn--active" : null)" /> + } + </BitStack> + + <BitStack Horizontal> + <div class="color-square selected" style="background-color: @category.Color"></div> + <BitToggleButton @bind-bind-IsChecked="isColorPickerOpen" + Variant="BitVariant.Outline" + OnClick=@ToggleColorPicker> + @Localizer[(nameof(AppStrings.CustomColor))] + </BitToggleButton> + </BitStack> + + @if (isColorPickerOpen) + { + <div class="color-picker-container"> + <BitColorPicker @bind-Color="category.Color" Id="catColorInput" ShowPreview="true"> + @Localizer[nameof(AppStrings.DefaultColorPicker)] + </BitColorPicker> + </div> + } + </BitStack> + <ValidationMessage For="() => category.Color" /> + <br /> + <BitStack Horizontal> + <BitButton ButtonType="BitButtonType.Button" Href="@Urls.CategoriesPage" Variant="BitVariant.Outline"> + @Localizer[nameof(AppStrings.Cancel)] + </BitButton> + + <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Save)] + </BitButton> + </BitStack> + </BitStack> + </EditForm> + </BitCard> + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.cs similarity index 80% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.cs index 3156812c7c..6fa4711de5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.cs @@ -1,20 +1,21 @@ using Boilerplate.Shared.Controllers.Categories; using Boilerplate.Shared.Dtos.Categories; -namespace Boilerplate.Client.Core.Components.Pages.Categories; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Categories; [Authorize] public partial class AddOrEditCategoryPage { + protected override string? Title => Localizer[nameof(AppStrings.Category)]; + protected override string? Subtitle => string.Empty; + [AutoInject] ICategoryController categoryController = default!; [Parameter] public Guid? Id { get; set; } private bool isLoading; private bool isSaving; - private string? saveMessage; private bool isColorPickerOpen; - private BitColor saveMessageColor; private CategoryDto category = new(); protected override async Task OnInitAsync() @@ -69,14 +70,11 @@ private async Task Save() } catch (ResourceValidationException e) { - saveMessageColor = BitColor.Error; - - saveMessage = string.Join(Environment.NewLine, e.Payload.Details.SelectMany(d => d.Errors).Select(e => e.Message)); + SnackBarService.Error(string.Join(Environment.NewLine, e.Payload.Details.SelectMany(d => d.Errors).Select(e => e.Message))); } catch (KnownException e) { - saveMessage = e.Message; - saveMessageColor = BitColor.Error; + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.scss new file mode 100644 index 0000000000..fc5ea58c44 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.scss @@ -0,0 +1,24 @@ +@import '../../../../Styles/abstracts/_bit-css-variables.scss'; + +.color-btn { + width: 2rem; + height: 2rem; + border: none; + outline: none; + cursor: pointer; + border-radius: 2px; +} + +.color-btn--active { + background-size: 20px; + background-position: center; + background-repeat: no-repeat; + background-image: url('images/icons/checkmark-icon.svg'); +} + +.color-square { + width: 5rem; + height: 2rem; + border-radius: 2px; + display: inline-block; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor new file mode 100644 index 0000000000..df85275898 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor @@ -0,0 +1,79 @@ +@attribute [Route(Urls.CategoriesPage)] +@attribute [Route("{culture?}" + Urls.CategoriesPage)] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.CategoriesPageTitle)]</PageTitle> + +<section> + <BitStack> + <BitButton IconName="@BitIconName.Add" ReversedIcon + OnClick="CreateCategory"> + @Localizer[nameof(AppStrings.AddCategory)] + </BitButton> + <BitStack Gap="0" FillContent> + <div class="grid-container"> + <BitDataGrid @ref="dataGrid" + Class="categories-grid" + TGridItem="CategoryDto" + Pagination="pagination" + ItemsProvider="categoriesProvider"> + <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Name)]" + Align="BitDataGridAlign.Left" + Property="c => c!.Name" + Class="name-col" + Sortable="true"> + <ColumnOptions> + <BitStack Horizontal> + <BitSearchBox @bind-Value="CategoryNameFilter" + AutoFocus DisableAnimation + Immediate DebounceTime="500" + Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" /> + @if (isLoading) + { + <BitEllipsisLoading CustomSize="32" /> + } + </BitStack> + </ColumnOptions> + </BitDataGridPropertyColumn> + + <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Products)]" + Property="p => p!.ProductsCount" + Align="BitDataGridAlign.Left" + Class="count-col" + Sortable="true" /> + + <BitDataGridTemplateColumn Title="@Localizer[nameof(AppStrings.Color)]" + Align="BitDataGridAlign.Left" + Context="category" + Class="color-col"> + <span class="color-box" style="background-color:@(category!.Color)"></span> + </BitDataGridTemplateColumn> + + <BitDataGridTemplateColumn Title="@Localizer[nameof(AppStrings.Action)]" + Align="BitDataGridAlign.Center" + Class="actions-col" + Context="category"> + <BitButton Variant="BitVariant.Text" + IconName="@BitIconName.Edit" + Title="@Localizer[(nameof(AppStrings.Edit))]" + OnClick="WrapHandled(() => EditCategory(category!))" /> + <BitButton Color="BitColor.Error" + Variant="BitVariant.Text" + IconName="@BitIconName.Delete" + Title="@Localizer[(nameof(AppStrings.Delete))]" + OnClick="WrapHandled(() => { isDeleteDialogOpen = true; deletingCategory = category; })" /> + </BitDataGridTemplateColumn> + </BitDataGrid> + </div> + <BitDataGridPaginator Value="pagination" /> + </BitStack> + </BitStack> +</section> + +<BitDialog OnOk="WrapHandled(DeleteCategory)" + @bind-IsOpen="isDeleteDialogOpen" + OkText="@Localizer[nameof(AppStrings.Yes)]" + CancelText="@Localizer[nameof(AppStrings.No)]" + Title="@Localizer[nameof(AppStrings.DeleteProduct)]" + Message="@Localizer.GetString(nameof(AppStrings.AreYouSureWannaDeleteProduct), deletingCategory?.Name ?? "")" /> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.cs similarity index 81% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.cs index 6f65ba52ab..4e0e93df6a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.cs @@ -2,18 +2,20 @@ using Boilerplate.Shared.Controllers.Categories; using Boilerplate.Shared.Dtos.Categories; -namespace Boilerplate.Client.Core.Components.Pages.Categories; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Categories; -[Authorize] public partial class CategoriesPage { + protected override string? Title => Localizer[nameof(AppStrings.Categories)]; + protected override string? Subtitle => string.Empty; + [AutoInject] ICategoryController categoryController = default!; private bool isLoading; - private string categoryNameFilter = string.Empty; - - private ConfirmMessageBox confirmMessageBox = default!; + private bool isDeleteDialogOpen; + private CategoryDto? deletingCategory; private BitDataGrid<CategoryDto>? dataGrid; + private string categoryNameFilter = string.Empty; private BitDataGridItemsProvider<CategoryDto> categoriesProvider = default!; private BitDataGridPaginationState pagination = new() { ItemsPerPage = 10 }; @@ -89,17 +91,20 @@ private void EditCategory(CategoryDto category) NavigationManager.NavigateTo($"{Urls.AddOrEditCategoryPage}/{category.Id}"); } - private async Task DeleteCategory(CategoryDto category) + private async Task DeleteCategory() { - var confirmed = await confirmMessageBox.Show(Localizer.GetString(nameof(AppStrings.AreYouSureWannaDeleteCategory), category.Name ?? string.Empty), - Localizer[nameof(AppStrings.DeleteCategory)]); + if (deletingCategory is null) return; - if (confirmed) + try { - await categoryController.Delete(category.Id, CurrentCancellationToken); + await categoryController.Delete(deletingCategory.Id, deletingCategory.ConcurrencyStamp.ToStampString(), CurrentCancellationToken); await RefreshData(); } + finally + { + deletingCategory = null; + } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.scss new file mode 100644 index 0000000000..2c20fa7e0e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.scss @@ -0,0 +1,83 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; +@import '../../../../Styles/abstracts/_bit-css-variables.scss'; + +section { + width: 100%; +} + +.grid-container { + overflow: auto; + height: calc(var(--app-height) - 14rem); + + @include lt-md { + height: calc(var(--app-height) - 17rem); + } + + @include lt-sm { + height: calc(var(--app-height) - 16rem); + } +} + +::deep { + .categories-grid { + width: 100%; + height: 100%; + border-spacing: 0; + background-color: $bit-color-background-secondary; + + .name-col { + padding-inline-start: 16px; + } + + .count-col { + width: 110px; + } + + .color-col { + width: 70px; + } + + .actions-col { + width: 95px; + } + + thead { + height: 44px; + background-color: $bit-color-background-tertiary; + } + + td { + height: 44px; + white-space: nowrap; + border-bottom: 1px solid $bit-color-border-tertiary; + } + + .col-options { + padding: 8px; + } + + .col-options-button { + cursor: pointer; + } + + .color-box { + width: 24px; + height: 24px; + display: block; + border-radius: 2px; + } + } + + .bitdatagrid-paginator { + padding: 8px; + font-size: 14px; + flex-wrap: nowrap; + padding-inline-start: 16px; + background-color: $bit-color-background-secondary; + + button { + cursor: pointer; + font-size: 12px; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor new file mode 100644 index 0000000000..02c23e89ce --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor @@ -0,0 +1,21 @@ +@attribute [Route(Urls.DashboardPage)] +@attribute [Route("{culture?}" + Urls.DashboardPage)] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.DashboardPageTitle)]</PageTitle> + +<section> + <BitStack Gap="3rem"> + <OverallStatsWidget /> + + <BitStack Horizontal Wrap> + @if (isLoadingAssemblies is false) + { + <ProductsCountPerCategoryWidget /> + <ProductsPercentageWidget /> + } + </BitStack> + </BitStack> +</section> + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.cs new file mode 100644 index 0000000000..6db19b111d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.cs @@ -0,0 +1,54 @@ +//+:cnd:noEmit +using Microsoft.AspNetCore.Components.WebAssembly.Services; + +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Dashboard; + +public partial class DashboardPage +{ + protected override string? Title => Localizer[nameof(AppStrings.Dashboard)]; + protected override string? Subtitle => Localizer[nameof(AppStrings.DashboardSubtitle)]; + + [AutoInject] LazyAssemblyLoader lazyAssemblyLoader = default!; + + private bool isLoadingAssemblies = true; + //#if (signalR == true) + private Action? unsubscribe; + //#endif + + protected async override Task OnInitAsync() + { + //#if (signalR == true) + unsubscribe = PubSubService.Subscribe(SharedPubSubMessages.DASHBOARD_DATA_CHANGED, async _ => + { + NavigationManager.NavigateTo(Urls.DashboardPage, replace: true); + }); + //#endif + try + { + if (AppPlatform.IsBrowser) + { + await lazyAssemblyLoader.LoadAssembliesAsync([ + //#if (offlineDb == false) + "System.Private.Xml.wasm", "System.Data.Common.wasm", + //#endif + "Newtonsoft.Json.wasm"] + ); + } + } + finally + { + isLoadingAssemblies = false; + } + + await base.OnInitAsync(); + } + + //#if (signalR == true) + protected override ValueTask DisposeAsync(bool disposing) + { + unsubscribe?.Invoke(); + + return base.DisposeAsync(disposing); + } + //#endif +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.scss new file mode 100644 index 0000000000..9f811b83b2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/DashboardPage.razor.scss @@ -0,0 +1,4 @@ +section { + width: 100%; + height: 100%; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor new file mode 100644 index 0000000000..7bb1567684 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor @@ -0,0 +1,14 @@ +@inherits AppComponentBase + +<section> + <BitStack> + <BitStack Horizontal Wrap> + <OverallStatusCard Loading="isLoading" Value="dto.TotalCategories" Label="@Localizer[nameof(AppStrings.TotalCategories)]" IconName="@BitIconName.BuildQueue" /> + <OverallStatusCard Loading="isLoading" Value="dto.CategoriesWithProductCount" Label="@Localizer[nameof(AppStrings.CategoriesWithProductCount)]" IconName="@BitIconName.BarChart3" /> + </BitStack> + <BitStack Horizontal Wrap> + <OverallStatusCard Loading="isLoading" Value="dto.TotalProducts" Label="@Localizer[nameof(AppStrings.TotalProducts)]" IconName="@BitIconName.Product" /> + <OverallStatusCard Loading="isLoading" Value="dto.Last30DaysProductCount" Label="@Localizer[nameof(AppStrings.Last30DaysProductCount)]" IconName="@BitIconName.BarChartVerticalFill" /> + </BitStack> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.cs similarity index 67% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.cs index f042672e4f..1acff3b25e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.cs @@ -1,14 +1,14 @@ using Boilerplate.Shared.Controllers.Dashboard; using Boilerplate.Shared.Dtos.Dashboard; -namespace Boilerplate.Client.Core.Components.Pages.Dashboard; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Dashboard; public partial class OverallStatsWidget { [AutoInject] IDashboardController dashboardController = default!; private bool isLoading; - private OverallAnalyticsStatsDataResponseDto data = new(); + private OverallAnalyticsStatsDataResponseDto dto = new(); protected override async Task OnInitAsync() { @@ -21,7 +21,7 @@ private async Task GetData() try { - data = await dashboardController.GetOverallAnalyticsStatsData(CurrentCancellationToken); + dto = await dashboardController.GetOverallAnalyticsStatsData(CurrentCancellationToken); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.scss new file mode 100644 index 0000000000..e50f359f65 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.scss @@ -0,0 +1,3 @@ +section { + width: 100%; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor new file mode 100644 index 0000000000..4b265d4270 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor @@ -0,0 +1,20 @@ +@inherits AppComponentBase + +<section> + <BitCard Border="BitColorKind.Transparent" FullWidth> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitIcon IconName="@IconName" Size="BitSize.Large" Style="background-color:var(--bit-clr-bg-ter);padding:1rem" /> + @if (Loading) + { + <BitEllipsisLoading Size="BitSize.Small" /> + } + else + { + <BitStack Gap="0"> + <BitText Typography="BitTypography.H6">@Value</BitText> + <BitText Typography="BitTypography.Body2" Color="BitColor.SecondaryForeground">@Label</BitText> + </BitStack> + } + </BitStack> + </BitCard> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.cs new file mode 100644 index 0000000000..738f281be3 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.cs @@ -0,0 +1,9 @@ +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Dashboard; + +public partial class OverallStatusCard +{ + [Parameter] public bool Loading { get; set; } + [Parameter] public int Value { get; set; } + [Parameter] public string? Label { get; set; } + [Parameter] public string? IconName { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.scss new file mode 100644 index 0000000000..b622b1da76 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.scss @@ -0,0 +1,9 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: calc(50% - 12px); + + @include lt-md { + width: 100%; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor new file mode 100644 index 0000000000..b61223ee7f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor @@ -0,0 +1,25 @@ +@inherits AppComponentBase + +<section> + <BitCard FullSize Border="BitColorKind.Transparent" Class="card"> + <BitStack Alignment="BitAlignment.Center" Gap="2rem"> + <BitStack Gap="0"> + <BitText Typography="BitTypography.H6">@Localizer[nameof(AppStrings.ProductsCountPerCategoryChart)]</BitText> + <BitText Typography="BitTypography.Body2"> + @Localizer[nameof(AppStrings.ProductsCountPerCategoryChartText)] + </BitText> + </BitStack> + + @if (isLoading) + { + <BitGridLoading Style="padding:2rem" /> + } + else + { + <BitChart Config="config" /> + } + </BitStack> + </BitCard> +</section> + + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.cs similarity index 91% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.cs index ae5e17bab8..a9fec72f26 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.cs @@ -1,13 +1,12 @@ using Boilerplate.Shared.Controllers.Dashboard; -namespace Boilerplate.Client.Core.Components.Pages.Dashboard; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Dashboard; public partial class ProductsCountPerCategoryWidget { [AutoInject] IDashboardController dashboardController = default!; private bool isLoading; - private BitChart? chart; private BitChartBarConfig config = default!; protected override async Task OnInitAsync() @@ -29,10 +28,10 @@ protected override async Task OnInitAsync() private async Task GetData() { + isLoading = true; + try { - isLoading = true; - var data = await dashboardController.GetProductsCountPerCategoryStats(CurrentCancellationToken); BitChartBarDataset<int> chartDataSet = [.. data.Select(d => d.ProductCount)]; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.scss new file mode 100644 index 0000000000..a81ad84682 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.scss @@ -0,0 +1,13 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: calc(50% - 12px); + + @include lt-md { + width: 100%; + } + + ::deep .card { + min-height: 333px; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor new file mode 100644 index 0000000000..ebb7b3b30d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor @@ -0,0 +1,25 @@ +@inherits AppComponentBase + +<section> + <BitCard FullSize Border="BitColorKind.Transparent" Class="card"> + <BitStack Alignment="BitAlignment.Center" Gap="2rem"> + <BitStack Gap="0"> + <BitText Typography="BitTypography.H6"> + @Localizer[nameof(AppStrings.ProductsPercentagePerCategory)] + </BitText> + <BitText Typography="BitTypography.Body2"> + @Localizer[nameof(AppStrings.ProductsPercentagePerCategoryText)] + </BitText> + </BitStack> + + @if (isLoading) + { + <BitGridLoading Style="padding:2rem" /> + } + else + { + <BitChart Config="config" /> + } + </BitStack> + </BitCard> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.cs similarity index 93% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.cs index c8f244aaba..a6e3745786 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.cs @@ -1,6 +1,6 @@ using Boilerplate.Shared.Controllers.Dashboard; -namespace Boilerplate.Client.Core.Components.Pages.Dashboard; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Dashboard; public partial class ProductsPercentageWidget { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.scss new file mode 100644 index 0000000000..a81ad84682 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.scss @@ -0,0 +1,13 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: calc(50% - 12px); + + @include lt-md { + width: 100%; + } + + ::deep .card { + min-height: 333px; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor new file mode 100644 index 0000000000..6a32773b9c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor @@ -0,0 +1,50 @@ +@attribute [Route(Urls.OfflineEditProfilePage)] +@attribute [Route("{culture?}" + Urls.OfflineEditProfilePage)] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.EditProfileTitle)]</PageTitle> + +<section> + <BitStack Alignment="BitAlignment.Center"> + <BitText Typography="BitTypography.H3"> + @Localizer[nameof(AppStrings.EditProfileTitle)] + </BitText> + + @if (isLoading) + { + <BitEllipsisLoading /> + } + + <EditForm Model="userToEdit" OnValidSubmit="WrapHandled(DoSave)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="userToEdit.FullName" + Label="@Localizer[nameof(AppStrings.FullName)]" + Placeholder="@Localizer[nameof(AppStrings.FullName)]" /> + <ValidationMessage For="@(() => userToEdit.FullName)" /> + + <BitDatePicker IsResponsive @bind-Value="userToEdit.BirthDate" + Class="edit-profile-dtp" + Label="@Localizer[nameof(AppStrings.BirthDate)]" + GoToTodayTitle="@Localizer[nameof(AppStrings.GoToToday)]" + Placeholder="@Localizer[nameof(AppStrings.SelectBirthDate)]" /> + <ValidationMessage For="@(() => userToEdit.BirthDate)" /> + + <BitChoiceGroup Horizontal + @bind-Value="userToEdit.Gender" + TItem="BitChoiceGroupOption<Gender>" TValue="Gender" + Label="@Localizer[nameof(AppStrings.Gender)]"> + <BitChoiceGroupOption Value="Gender.Male" Text="@Localizer[nameof(AppStrings.GenderMale)]" /> + <BitChoiceGroupOption Value="Gender.Female" Text="@Localizer[nameof(AppStrings.GenderFemale)]" /> + <BitChoiceGroupOption Value="Gender.Other" Text="@Localizer[nameof(AppStrings.GenderOther)]" /> + </BitChoiceGroup> + + <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit" Title="@Localizer[nameof(AppStrings.Save)]"> + @Localizer[nameof(AppStrings.Save)] + </BitButton> + </BitStack> + </EditForm> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.cs similarity index 79% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.cs index ca633680c6..a33e052819 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.cs @@ -2,17 +2,17 @@ using Boilerplate.Shared.Dtos.Identity; using Microsoft.EntityFrameworkCore; -namespace Boilerplate.Client.Core.Components.Pages.Offline; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Offline; -[Authorize] public partial class OfflineEditProfilePage { + protected override string? Title => Localizer[nameof(AppStrings.ProfileTitle)]; + protected override string? Subtitle => string.Empty; + [AutoInject] IDbContextFactory<OfflineDbContext> dbContextFactory = default!; private bool isSaving; private bool isLoading = true; - private string? editProfileMessage; - private BitColor editProfileMessageColor; private UserDto user = new(); private readonly EditUserDto userToEdit = new(); @@ -51,7 +51,6 @@ private async Task DoSave() if (isSaving) return; isSaving = true; - editProfileMessage = null; try { @@ -61,14 +60,11 @@ private async Task DoSave() dbContext.Users.Update(user); await dbContext.SaveChangesAsync(CurrentCancellationToken); - editProfileMessageColor = BitColor.Success; - editProfileMessage = Localizer[nameof(AppStrings.ProfileUpdatedSuccessfullyMessage)]; + SnackBarService.Success(Localizer[nameof(AppStrings.ProfileUpdatedSuccessfullyMessage)]); } catch (KnownException e) { - editProfileMessageColor = BitColor.Error; - - editProfileMessage = e.Message; + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.scss new file mode 100644 index 0000000000..7b1d922f13 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.scss @@ -0,0 +1,8 @@ +section { +} + +::deep { + form { + width: min(25rem, 100%); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor new file mode 100644 index 0000000000..7da5968105 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor @@ -0,0 +1,69 @@ +@inherits AppComponentBase + +<BitModal @bind-IsOpen="isOpen" Blocking AutoToggleScroll="false"> + <section> + <BitStack FillContent> + <BitStack FillContent> + <BitStack Horizontal VerticalAlign="BitAlignment.Center" Class="stack"> + <BitText Typography="BitTypography.H5"> + @if (product.Id != default) + { + @Localizer[nameof(AppStrings.EditProduct)] + } + else + { + @Localizer[nameof(AppStrings.AddProduct)] + } + </BitText> + <BitSpacer /> + <BitButton IconName="@BitIconName.ChromeClose" + Color="BitColor.SecondaryForeground" + OnClick="CloseModal" + Variant="BitVariant.Text" /> + </BitStack> + + <BitSeparator /> + </BitStack> + + <EditForm Model="product" OnValidSubmit="WrapHandled(Save)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent Class="stack"> + <BitTextField @bind-Value="product.Name" + AutoComplete="@BitAutoCompleteValue.Off" + Label="@Localizer[(nameof(AppStrings.Name))]" + Placeholder="@Localizer[nameof(AppStrings.EnterProductName)]" /> + <ValidationMessage For="() => product.Name" /> + + <BitDropdown @bind-Value="selectedCategoryId" + Items="allCategoryList" + Label="@Localizer[(nameof(AppStrings.Category))]" + Placeholder="@Localizer[(nameof(AppStrings.SelectCategory))]" + OnSelectItem="((BitDropdownItem<string> item) => { product.CategoryId = Guid.Parse(item.Value!); product.CategoryName = item.Text; })" /> + <ValidationMessage For="@(() => product.CategoryId)" /> + + <BitNumberField @bind-Value="product.Price" + Suffix="$" + NumberFormat="C2" + Label="@Localizer[(nameof(AppStrings.Price))]" /> + <ValidationMessage For="() => product.Price" /> + + <BitTextField @bind-Value="product.Description" + Rows="3" + IsMultiline="true" + Label="@Localizer[(nameof(AppStrings.Description))]" /> + <ValidationMessage For="() => product.Description" /> + + <BitStack Horizontal HorizontalAlign="BitAlignment.End"> + <BitButton ButtonType="BitButtonType.Button" OnClick="CloseModal" Variant="BitVariant.Outline"> + @Localizer[nameof(AppStrings.Cancel)] + </BitButton> + <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Save)] + </BitButton> + </BitStack> + </BitStack> + </EditForm> + </BitStack> + </section> +</BitModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.cs similarity index 85% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.cs index 089d2c8128..df7bb6c2af 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.cs @@ -2,7 +2,7 @@ using Boilerplate.Shared.Controllers.Product; using Boilerplate.Shared.Dtos.Products; -namespace Boilerplate.Client.Core.Components.Pages.Products; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Products; public partial class AddOrEditProductModal { @@ -10,17 +10,17 @@ public partial class AddOrEditProductModal [AutoInject] IProductController productController = default!; private bool isOpen; - private bool isLoading; private bool isSaving; + private bool isLoading; private ProductDto product = new(); + private string selectedCategoryId = string.Empty; private List<BitDropdownItem<string>> allCategoryList = []; - private string selectedCategoyId = string.Empty; [Parameter] public EventCallback OnSave { get; set; } protected override async Task OnInitAsync() { - await LoadAllCategoriesAsync(); + await LoadAllCategories(); } public async Task ShowModal(ProductDto productToShow) @@ -29,13 +29,13 @@ await InvokeAsync(() => { isOpen = true; product = productToShow; - selectedCategoyId = (product.CategoryId ?? default).ToString(); + selectedCategoryId = (product.CategoryId ?? default).ToString(); StateHasChanged(); }); } - private async Task LoadAllCategoriesAsync() + private async Task LoadAllCategories() { isLoading = true; @@ -83,7 +83,7 @@ private async Task Save() } } - private async Task OnCloseClick() + private async Task CloseModal() { isOpen = false; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.scss new file mode 100644 index 0000000000..b69d9563a9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/AddOrEditProductModal.razor.scss @@ -0,0 +1,10 @@ +section { + width: 90vw; + max-width: 35rem; + border-radius: 4px; + padding-block: 1rem; +} + +::deep .stack { + padding-inline: 1rem; +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor new file mode 100644 index 0000000000..937d6a595d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor @@ -0,0 +1,93 @@ +@attribute [Route(Urls.ProductsPage)] +@attribute [Route("{culture?}" + Urls.ProductsPage)] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.ProductsPageTitle)]</PageTitle> + +<section> + <BitStack> + <BitButton IconName="@BitIconName.Add" ReversedIcon + OnClick="WrapHandled(CreateProduct)"> + @Localizer[nameof(AppStrings.AddProduct)] + </BitButton> + <BitStack Gap="0" FillContent> + <div class="grid-container"> + <BitDataGrid @ref="dataGrid" + Class="products-grid" + TGridItem="ProductDto" + Pagination="pagination" + ItemsProvider="productsProvider"> + <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Name)]" + Property="p => p!.Name" + Class="name-col" + Sortable="true"> + <ColumnOptions> + <BitStack Horizontal> + <BitSearchBox @bind-Value="ProductNameFilter" + AutoFocus DisableAnimation + Immediate DebounceTime="500" + Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" /> + @if (isLoading) + { + <BitEllipsisLoading CustomSize="32" /> + } + </BitStack> + </ColumnOptions> + </BitDataGridPropertyColumn> + + <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Category)]" + Property="p => p!.CategoryName" + Align="BitDataGridAlign.Left" + Class="category-col" + Sortable="true"> + <ColumnOptions> + <BitStack Horizontal> + <BitSearchBox @bind-Value="CategoryNameFilter" + AutoFocus DisableAnimation + Immediate DebounceTime="500" + Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" /> + @if (isLoading) + { + <BitEllipsisLoading CustomSize="32" /> + } + </BitStack> + </ColumnOptions> + </BitDataGridPropertyColumn> + + <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Price)]" + Align="BitDataGridAlign.Left" + Property="p => p!.Price" + Class="price-col" + Sortable="true" + Format="C2" /> + + <BitDataGridTemplateColumn Title="@Localizer[(nameof(AppStrings.Action))]" + Align="BitDataGridAlign.Center" + Class="actions-col" + Context="product"> + <BitButton Variant="BitVariant.Text" + IconName="@BitIconName.Edit" + Title="@Localizer[(nameof(AppStrings.Edit))]" + OnClick="WrapHandled(() => EditProduct(product!))" /> + <BitButton Color="BitColor.Error" + Variant="BitVariant.Text" + IconName="@BitIconName.Delete" + Title="@Localizer[(nameof(AppStrings.Delete))]" + OnClick="WrapHandled(() => { isDeleteDialogOpen = true; deletingProduct = product; })" /> + </BitDataGridTemplateColumn> + </BitDataGrid> + </div> + <BitDataGridPaginator Value="pagination" /> + </BitStack> + </BitStack> +</section> + +<BitDialog OnOk="WrapHandled(DeleteProduct)" + @bind-IsOpen="isDeleteDialogOpen" + OkText="@Localizer[nameof(AppStrings.Yes)]" + CancelText="@Localizer[nameof(AppStrings.No)]" + Title="@Localizer[nameof(AppStrings.DeleteProduct)]" + Message="@Localizer.GetString(nameof(AppStrings.AreYouSureWannaDeleteProduct), deletingProduct?.Name ?? "")" /> + +<AddOrEditProductModal @ref="modal" OnSave="WrapHandled(RefreshData)" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.cs similarity index 83% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.cs index d306e490f0..055df432d0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.cs @@ -2,23 +2,29 @@ using Boilerplate.Shared.Controllers.Product; using Boilerplate.Shared.Dtos.Products; -namespace Boilerplate.Client.Core.Components.Pages.Products; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Products; -[Authorize] public partial class ProductsPage { + protected override string? Title => Localizer[nameof(AppStrings.Products)]; + protected override string? Subtitle => string.Empty; + + [AutoInject] IProductController productController = default!; + private bool isLoading; + private bool isDeleteDialogOpen; + private ProductDto? deletingProduct; private AddOrEditProductModal? modal; private string productNameFilter = string.Empty; private string categoryNameFilter = string.Empty; - private ConfirmMessageBox confirmMessageBox = default!; private BitDataGrid<ProductDto>? dataGrid; private BitDataGridItemsProvider<ProductDto> productsProvider = default!; private BitDataGridPaginationState pagination = new() { ItemsPerPage = 10 }; + string ProductNameFilter { get => productNameFilter; @@ -39,6 +45,7 @@ string CategoryNameFilter } } + protected override async Task OnInitAsync() { PrepareGridDataProvider(); @@ -106,17 +113,20 @@ private async Task EditProduct(ProductDto product) await modal!.ShowModal(product); } - private async Task DeleteProduct(ProductDto product) + private async Task DeleteProduct() { - var confirmed = await confirmMessageBox.Show(Localizer.GetString(nameof(AppStrings.AreYouSureWannaDeleteProduct), product.Name ?? string.Empty), - Localizer[nameof(AppStrings.DeleteProduct)]); + if (deletingProduct is null) return; - if (confirmed) + try { - await productController.Delete(product.Id, CurrentCancellationToken); + await productController.Delete(deletingProduct.Id, deletingProduct.ConcurrencyStamp.ToStampString(), CurrentCancellationToken); await RefreshData(); } + finally + { + deletingProduct = null; + } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.scss new file mode 100644 index 0000000000..3c36cb4352 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Products/ProductsPage.razor.scss @@ -0,0 +1,89 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; +@import '../../../../Styles/abstracts/_bit-css-variables.scss'; + +section { + width: 100%; +} + +.grid-container { + overflow: auto; + height: calc(var(--app-height) - 14rem); + + @include lt-md { + height: calc(var(--app-height) - 17rem); + } + + @include lt-sm { + height: calc(var(--app-height) - 16rem); + } +} + +::deep { + .products-grid { + width: 100%; + height: 100%; + border-spacing: 0; + background-color: $bit-color-background-secondary; + + .name-col { + padding-inline-start: 16px; + } + + .category-col { + width: 135px; + } + + .price-col { + width: 135px; + } + + .actions-col { + width: 95px; + } + + thead { + height: 44px; + background-color: $bit-color-background-tertiary; + } + + td { + height: 44px; + white-space: nowrap; + border-bottom: 1px solid $bit-color-border-tertiary; + } + + .col-options { + padding: 8px; + } + + .col-options-button { + cursor: pointer; + } + + th:nth-child(2) .col-options-button { + cursor: pointer; + background-image: none; + + &:before { + content: "\E721"; + font-style: normal; + font-weight: normal; + display: inline-block; + font-family: 'Fabric MDL2'; + } + } + } + + .bitdatagrid-paginator { + padding: 8px; + font-size: 14px; + flex-wrap: nowrap; + padding-inline-start: 16px; + background-color: $bit-color-background-secondary; + + button { + cursor: pointer; + font-size: 12px; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor new file mode 100644 index 0000000000..50d3ebaa07 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor @@ -0,0 +1,22 @@ +@inherits AppComponentBase + +<BitAccordion OnClick="HandleOnClick" IsExpanded="Value == Name" + Classes="@(new() { Header=BitCss.Class.Color.Background.Secondary })"> + + <HeaderTemplate> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitStack Gap="0.5rem"> + <BitText Typography="BitTypography.Subtitle1">@Title</BitText> + <BitText Color="BitColor.SecondaryForeground" Typography="BitTypography.Body2"> + @Subtitle + </BitText> + </BitStack> + + <BitIcon IconName="@(Value == Name ? BitIconName.ChevronUp : BitIconName.ChevronDown)" Color="BitColor.Tertiary" /> + </BitStack> + </HeaderTemplate> + + <ChildContent> + @ChildContent + </ChildContent> +</BitAccordion> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor.cs new file mode 100644 index 0000000000..611042506d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/Accordion.razor.cs @@ -0,0 +1,21 @@ +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; + +public partial class Accordion +{ + [Parameter] public string? Name { get; set; } + + [Parameter] public string? Value { get; set; } + [Parameter] public EventCallback<string?> ValueChanged { get; set; } + + [Parameter] public string? Title { get; set; } + [Parameter] public string? Subtitle { get; set; } + + [Parameter] public RenderFragment? ChildContent { get; set; } + + + private async Task HandleOnClick() + { + Value = Value == Name ? null : Name; + await ValueChanged.InvokeAsync(Value); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor new file mode 100644 index 0000000000..ec7c7557e1 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor @@ -0,0 +1,97 @@ +@inherits AppComponentBase + +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + @if (showConfirmation is false) + { + <EditForm Model="sendModel" OnValidSubmit="WrapHandled(SendToken)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + @if (Email is not null) + { + <BitTag Variant="BitVariant.Outline" Text="@Email" Color="BitColor.Info" /> + } + + <BitTextField @bind-Value="sendModel.Email" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.NewEmail)]" + Placeholder="@Localizer[nameof(AppStrings.NewEmailPlaceholder)]" /> + <ValidationMessage For="@(() => sendModel.Email)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Submit)] + </BitButton> + + <div> + @Localizer[nameof(AppStrings.ConfirmMessageInProfile)] + <BitButton ButtonType="BitButtonType.Button" + Variant="BitVariant.Text" + OnClick="() => showConfirmation = true"> + @Localizer[nameof(AppStrings.Confirm)] + </BitButton> + </div> + <br /> + </BitStack> + </EditForm> + } + else + { + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.ConfirmEmailSubtitle)] + <br /> + @Localizer[nameof(AppStrings.ConfirmEmailMessage)] + </BitText> + + <EditForm Model="changeModel" OnValidSubmit="WrapHandled(ChangeEmail)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="changeModel.Email" + IsEnabled="isEmailUnavailable" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.Email)]" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => changeModel.Email)" /> + + <BitTextField @bind-Value="changeModel.Token" + Type="BitInputType.Number" + Label="@Localizer[nameof(AppStrings.EmailToken)]" + Placeholder="@Localizer[nameof(AppStrings.EmailTokenPlaceholder)]" /> + <ValidationMessage For="@(() => changeModel.Token)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.EmailTokenConfirmButtonText)] + </BitButton> + + <div> + <BitButton ButtonType="BitButtonType.Button" + Variant="BitVariant.Text" + IconName="@BitIconName.Back" + OnClick="GoBack"> + @Localizer[nameof(AppStrings.GoBack)] + </BitButton> + </div> + </BitStack> + </EditForm> + + @if (isEmailUnavailable is false) + { + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.NotReceivedEmailMessage)] + </BitText> + + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.CheckSpamMailMessage)] + </BitText> + + <BitButton Variant="BitVariant.Outline" + OnClick="WrapHandled(SendToken)" + ButtonType="BitButtonType.Button"> + @Localizer[nameof(AppStrings.ResendEmailTokenButtonText)] + </BitButton> + } + <br /> + } + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.cs similarity index 72% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.cs index c5fcb1c751..bda871627d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.cs @@ -1,25 +1,10 @@ -using Boilerplate.Shared.Dtos.Identity; -using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Dtos.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; public partial class ChangeEmailSection { - private bool isWaiting; - private string? message; - private BitColor messageColor; - private bool showConfirmation; - private bool isEmailUnavailable = true; - private ElementReference messageRef = default!; - private readonly ChangeEmailRequestDto changeModel = new(); - private readonly SendEmailTokenRequestDto sendModel = new(); - - - [AutoInject] private IUserController userController = default!; - - - [Parameter] public bool Loading { get; set; } - [Parameter] public string? Email { get; set; } [Parameter, SupplyParameterFromQuery(Name = "email")] @@ -28,6 +13,17 @@ public partial class ChangeEmailSection [Parameter, SupplyParameterFromQuery(Name = "emailToken")] public string? EmailTokenQueryString { get; set; } + + [AutoInject] private IUserController userController = default!; + + + private bool isWaiting; + private bool showConfirmation; + private bool isEmailUnavailable = true; + private readonly ChangeEmailRequestDto changeModel = new(); + private readonly SendEmailTokenRequestDto sendModel = new(); + + protected override async Task OnInitAsync() { if (string.IsNullOrEmpty(EmailQueryString) is false) @@ -50,12 +46,12 @@ protected override async Task OnInitAsync() await base.OnInitAsync(); } + private async Task SendToken() { if (isWaiting || sendModel.Email == Email) return; isWaiting = true; - message = null; try { @@ -65,15 +61,11 @@ private async Task SendToken() isEmailUnavailable = false; changeModel.Email = sendModel.Email; - messageColor = BitColor.Success; - message = Localizer[nameof(AppStrings.SuccessfulSendChangeEmailTokenMessage)]; - await messageRef.ScrollIntoView(); + SnackBarService.Success(Localizer[nameof(AppStrings.SuccessfulSendChangeEmailTokenMessage)]); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { @@ -86,19 +78,16 @@ private async Task ChangeEmail() if (isWaiting) return; isWaiting = true; - message = null; try { await userController.ChangeEmail(changeModel, CurrentCancellationToken); - NavigationManager.NavigateTo(Urls.ProfilePage); + NavigationManager.NavigateTo($"{Urls.SettingsPage}/{Urls.SettingsSections.Account}", forceLoad: true); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.scss new file mode 100644 index 0000000000..ca0af593d5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangeEmailSection.razor.scss @@ -0,0 +1,10 @@ +section { + width: 100%; +} + +::deep { + form { + width: 100%; + max-width: 27rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor new file mode 100644 index 0000000000..7df89cd0f7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor @@ -0,0 +1,94 @@ +@inherits AppComponentBase + +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + @if (showConfirmation is false) + { + <EditForm Model="sendModel" OnValidSubmit="WrapHandled(SendToken)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + @if (PhoneNumber is not null) + { + <BitTag Variant="BitVariant.Outline" Text="@PhoneNumber" Color="BitColor.Info" /> + } + + <BitTextField @bind-Value="sendModel.PhoneNumber" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.NewPhoneNumber)]" + Placeholder="@Localizer[nameof(AppStrings.NewPhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => sendModel.PhoneNumber)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Submit)] + </BitButton> + + <div> + @Localizer[nameof(AppStrings.ConfirmMessageInProfile)] + <BitButton ButtonType="BitButtonType.Button" + Variant="BitVariant.Text" + OnClick="() => showConfirmation = true"> + @Localizer[nameof(AppStrings.Confirm)] + </BitButton> + </div> + <br /> + </BitStack> + </EditForm> + } + else + { + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.ConfirmPhoneSubtitle)] + <br /> + @Localizer[nameof(AppStrings.ConfirmPhoneMessage)] + </BitText> + + <EditForm Model="changeModel" OnValidSubmit="WrapHandled(ChangePhoneNumber)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="changeModel.PhoneNumber" + IsEnabled="isPhoneNumberUnavailable" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => changeModel.PhoneNumber)" /> + + <BitTextField @bind-Value="changeModel.Token" + Type="BitInputType.Number" + Label="@Localizer[nameof(AppStrings.PhoneToken)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneTokenPlaceholder)]" /> + <ValidationMessage For="@(() => changeModel.Token)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.PhoneTokenConfirmButtonText)] + </BitButton> + + <div> + <BitButton ButtonType="BitButtonType.Button" + Variant="BitVariant.Text" + IconName="@BitIconName.Back" + OnClick="GoBack"> + @Localizer[nameof(AppStrings.GoBack)] + </BitButton> + </div> + </BitStack> + </EditForm> + + @if (isPhoneNumberUnavailable is false) + { + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.NotReceivedPhoneMessage)] + </BitText> + + <BitButton IsLoading="isWaiting" + ButtonType="BitButtonType.Button" + Variant="BitVariant.Outline" + OnClick="WrapHandled(SendToken)"> + @Localizer[nameof(AppStrings.ResendPhoneTokenButtonText)] + </BitButton> + } + <br /> + } + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.cs similarity index 73% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.cs index b65b154433..d4b0d50a0f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.cs @@ -1,25 +1,10 @@ -using Boilerplate.Shared.Dtos.Identity; -using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Dtos.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; public partial class ChangePhoneNumberSection { - private bool isWaiting; - private string? message; - private BitColor messageColor; - private bool showConfirmation; - private bool isPhoneNumberUnavailable = true; - private ElementReference messageRef = default!; - private readonly SendPhoneTokenRequestDto sendModel = new(); - private readonly ChangePhoneNumberRequestDto changeModel = new(); - - - [AutoInject] private IUserController userController = default!; - - - [Parameter] public bool Loading { get; set; } - [Parameter] public string? PhoneNumber { get; set; } [Parameter, SupplyParameterFromQuery(Name = "phoneNumber")] @@ -28,6 +13,17 @@ public partial class ChangePhoneNumberSection [Parameter, SupplyParameterFromQuery(Name = "phoneToken")] public string? PhoneNumberTokenQueryString { get; set; } + + [AutoInject] private IUserController userController = default!; + + + private bool isWaiting; + private bool showConfirmation; + private bool isPhoneNumberUnavailable = true; + private readonly SendPhoneTokenRequestDto sendModel = new(); + private readonly ChangePhoneNumberRequestDto changeModel = new(); + + protected override async Task OnInitAsync() { if (string.IsNullOrEmpty(PhoneNumberQueryString) is false) @@ -50,12 +46,12 @@ protected override async Task OnInitAsync() await base.OnInitAsync(); } + private async Task SendToken() { if (isWaiting || sendModel.PhoneNumber == PhoneNumber) return; isWaiting = true; - message = null; try { @@ -65,15 +61,11 @@ private async Task SendToken() isPhoneNumberUnavailable = false; changeModel.PhoneNumber = sendModel.PhoneNumber; - messageColor = BitColor.Success; - message = Localizer[nameof(AppStrings.SuccessfulSendChangePhoneNumberTokenMessage)]; - await messageRef.ScrollIntoView(); + SnackBarService.Success(Localizer[nameof(AppStrings.SuccessfulSendChangePhoneNumberTokenMessage)]); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { @@ -86,19 +78,16 @@ private async Task ChangePhoneNumber() if (isWaiting) return; isWaiting = true; - message = null; try { await userController.ChangePhoneNumber(changeModel, CurrentCancellationToken); - NavigationManager.NavigateTo(Urls.ProfilePage); + NavigationManager.NavigateTo($"{Urls.SettingsPage}/{Urls.SettingsSections.Account}", forceLoad: true); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.scss new file mode 100644 index 0000000000..ca0af593d5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.scss @@ -0,0 +1,10 @@ +section { + width: 100%; +} + +::deep { + form { + width: 100%; + max-width: 27rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor new file mode 100644 index 0000000000..984e417909 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor @@ -0,0 +1,27 @@ +@inherits AppComponentBase + +<section> + <BitStack FillContent HorizontalAlign="BitAlignment.Center" Class="stack"> + <BitText Typography="BitTypography.H6" Color="BitColor.Error" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.DeleteAccount)] + </BitText> + + <BitText Typography="BitTypography.Body1"> + @Localizer[nameof(AppStrings.DeleteAccountPrompt)] + </BitText> + + <BitButton Color="BitColor.Error" + Variant="BitVariant.Outline" + OnClick="() => isDialogOpen = true"> + @Localizer[nameof(AppStrings.DeleteAccount)] + </BitButton> + <br /> + </BitStack> +</section> + +<BitDialog OnOk="WrapHandled(DeleteAccount)" + @bind-IsOpen="isDialogOpen" + OkText="@Localizer[nameof(AppStrings.Yes)]" + CancelText="@Localizer[nameof(AppStrings.No)]" + Title="@Localizer[nameof(AppStrings.DeleteAccount)]" + Message="@Localizer[nameof(AppStrings.DeleteAccountPrompt)]" /> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.cs new file mode 100644 index 0000000000..5e31176cd5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.cs @@ -0,0 +1,19 @@ +using Boilerplate.Shared.Controllers.Identity; + +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; + +public partial class DeleteAccountSection +{ + private bool isDialogOpen; + + + [AutoInject] IUserController userController = default!; + + + private async Task DeleteAccount() + { + await userController.Delete(CurrentCancellationToken); + + await AuthenticationManager.SignOut(CurrentCancellationToken); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.scss new file mode 100644 index 0000000000..ec82ca4dcc --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/DeleteAccountSection.razor.scss @@ -0,0 +1,11 @@ +section { + width: 100%; + display: flex; + justify-content: center; +} + +::deep { + .stack { + max-width: 27rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor new file mode 100644 index 0000000000..a2d677d5c8 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor @@ -0,0 +1,92 @@ +@inherits AppComponentBase + +@{ + var imageUrl = User?.ProfileImageName is null ? null : $"{profileImageUrl}&file={User.ProfileImageName}"; +} + +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + @if (Loading) + { + <BitShimmer Shape="BitShimmerShape.Circle" Height="4.5rem" /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="12.5rem" /> + <br /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="25rem" /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="25rem" /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="25rem" /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="25rem" /> + } + else + { + <BitFileUpload Accept="image/*" AutoUpload + MaxSize="1024 * 1024 * 10" + UploadUrl="@profileImageUploadUrl" + OnUploading="() => isUploading = true" + OnUploadFailed="WrapHandled(HandleOnUploadFailed)" + OnUploadComplete="WrapHandled(HandleOnUploadComplete)"> + <LabelTemplate> + <BitStack HorizontalAlign="BitAlignment.Center" Style="cursor:pointer"> + @if (isUploading is false) + { + <BitPersona HidePersonaDetails + ImageUrl="@imageUrl" + PrimaryText="@User?.FullName" + Size=@BitPersonaSize.Size72 + Presence="@(IsOnline is null ? BitPersonaPresence.None : IsOnline is true ? BitPersonaPresence.Online : BitPersonaPresence.Offline)" /> + <BitText Color="BitColor.Primary"> + @Localizer[nameof(AppStrings.UploadNewProfileImage)] + </BitText> + } + else + { + <BitShimmer Shape="BitShimmerShape.Circle" Height="4.5rem" /> + <BitShimmer Shape="BitShimmerShape.Rectangle" Height="1.5rem" Width="12.5rem" /> + } + </BitStack> + </LabelTemplate> + <FileViewTemplate></FileViewTemplate> + </BitFileUpload> + + @if (User?.ProfileImageName is not null) + { + <BitButton OnClick="RemoveProfileImage" Color="BitColor.Error"> + @Localizer[nameof(AppStrings.Remove)] + </BitButton> + } + <br /> + <EditForm Model="editUserDto" OnValidSubmit="WrapHandled(SaveProfile)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="editUserDto.FullName" + Label="@Localizer[nameof(AppStrings.FullName)]" + Placeholder="@Localizer[nameof(AppStrings.FullName)]" /> + <ValidationMessage For="@(() => editUserDto.FullName)" /> + + <BitDatePicker IsResponsive @bind-Value="editUserDto.BirthDate" + Class="edit-profile-dtp" + Label="@Localizer[nameof(AppStrings.BirthDate)]" + GoToTodayTitle="@Localizer[nameof(AppStrings.GoToToday)]" + Placeholder="@Localizer[nameof(AppStrings.SelectBirthDate)]" /> + <ValidationMessage For="@(() => editUserDto.BirthDate)" /> + + <BitChoiceGroup Horizontal + @bind-Value="editUserDto.Gender" + TItem="BitChoiceGroupOption<Gender>" TValue="Gender" + Label="@Localizer[nameof(AppStrings.Gender)]"> + <BitChoiceGroupOption Value="@Gender.Male" Text="@Localizer[nameof(AppStrings.GenderMale)]" /> + <BitChoiceGroupOption Value="@Gender.Female" Text="@Localizer[nameof(AppStrings.GenderFemale)]" /> + <BitChoiceGroupOption Value="@Gender.Other" Text="@Localizer[nameof(AppStrings.GenderOther)]" /> + </BitChoiceGroup> + + <BitButton IsLoading="isSaving" + ButtonType="BitButtonType.Submit" + Title="@Localizer[nameof(AppStrings.Save)]"> + @Localizer[nameof(AppStrings.Save)] + </BitButton> + </BitStack> + </EditForm> + } + <br /> + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs similarity index 55% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index 13d9eb6e3f..1eaea48056 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -1,46 +1,28 @@ -using Boilerplate.Shared.Dtos.Identity; -using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Dtos.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; -public partial class UserDataSection +public partial class ProfileSection { + [Parameter] public bool Loading { get; set; } + [Parameter] public UserDto? User { get; set; } + + + [AutoInject] private IUserController userController = default!; + + private bool isSaving; - private bool isRemoving; private bool isUploading; private string? profileImageUrl; - private string? profileImageError; private string? profileImageUploadUrl; private string? removeProfileImageHttpUrl; - - private UserDto user = default!; private readonly EditUserDto editUserDto = new(); - private string? message; - private BitColor messageColor; - private ElementReference messageRef = default!; - - - [AutoInject] private IUserController userController = default!; - - - [Parameter] public bool Loading { get; set; } - - [Parameter] - public UserDto User - { - get => user; - set - { - user = value; - user?.Patch(editUserDto); - } - } - protected override async Task OnInitAsync() { - var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessTokenAsync); + var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); removeProfileImageHttpUrl = $"api/Attachment/RemoveProfileImage?access_token={access_token}"; @@ -51,30 +33,33 @@ protected override async Task OnInitAsync() await base.OnInitAsync(); } + protected override void OnParametersSet() + { + User?.Patch(editUserDto); + + base.OnParametersSet(); + } + + private async Task SaveProfile() { - if (isSaving) return; + if (isSaving || User is null) return; isSaving = true; - message = null; try { - editUserDto.Patch(user); + editUserDto.Patch(User); - (await userController.Update(editUserDto, CurrentCancellationToken)).Patch(user); + (await userController.Update(editUserDto, CurrentCancellationToken)).Patch(User); PublishUserDataUpdated(); - messageColor = BitColor.Success; - message = Localizer[nameof(AppStrings.ProfileUpdatedSuccessfullyMessage)]; - await messageRef.ScrollIntoView(); + SnackBarService.Success(Localizer[nameof(AppStrings.ProfileUpdatedSuccessfullyMessage)]); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { @@ -84,45 +69,37 @@ private async Task SaveProfile() private async Task RemoveProfileImage() { - if (isRemoving) return; - - isRemoving = true; + if (isSaving || User is null) return; try { await HttpClient.DeleteAsync(removeProfileImageHttpUrl, CurrentCancellationToken); - user.ProfileImageName = null; + User.ProfileImageName = null; PublishUserDataUpdated(); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); - } - finally - { - isRemoving = false; + SnackBarService.Error(e.Message); } } private async Task HandleOnUploadComplete() { + if (User is null) return; + try { var updatedUser = await userController.GetCurrentUser(CurrentCancellationToken); - user.ProfileImageName = updatedUser.ProfileImageName; + User.ProfileImageName = updatedUser.ProfileImageName; PublishUserDataUpdated(); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { @@ -130,8 +107,14 @@ private async Task HandleOnUploadComplete() } } + private async Task HandleOnUploadFailed() + { + isUploading = false; + SnackBarService.Error(Localizer[nameof(AppStrings.FileUploadFailed)]); + } + private void PublishUserDataUpdated() { - PubSubService.Publish(PubSubMessages.USER_DATA_UPDATED, user); + PubSubService.Publish(ClientPubSubMessages.PROFILE_UPDATED, User); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.scss new file mode 100644 index 0000000000..ca0af593d5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.scss @@ -0,0 +1,10 @@ +section { + width: 100%; +} + +::deep { + form { + width: 100%; + max-width: 27rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor new file mode 100644 index 0000000000..f17ffc34c5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor @@ -0,0 +1,47 @@ +@inherits AppComponentBase + +<section> + <BitStack> + @if (currentSession is not null) + { + <BitText>@Localizer[nameof(AppStrings.CurrentSession)]</BitText> + <BitCard FullWidth> + <BitPersona PrimaryText="@(currentSession.Device ?? Localizer[nameof(AppStrings.UnknownDevice)])" + SecondaryText="@currentSession.Address" + TertiaryText="@($"{currentSession.IP} - {GetLastSeenOn(currentSession.RenewedOn)}")" + Size="BitPersonaSize.Size48" + Styles="@(new() { Image = "width:50%;height:50%" })" + ImageInitials="✓" + Presence="@(IsOnline is null ? BitPersonaPresence.None : IsOnline is true ? BitPersonaPresence.Online : BitPersonaPresence.Offline)" + ImageUrl="@($"/_content/Boilerplate.Client.Core/images/os/{GetImageUrl(currentSession.Device)}")" /> + </BitCard> + } + + @if (otherSessions is not null && otherSessions.Any()) + { + <br /> + <BitText>@Localizer[nameof(AppStrings.OtherSessions)]</BitText> + + <BitBasicList Items="otherSessions" EnableVirtualization Class="sessions-list"> + <RowTemplate Context="session"> + <BitCard FullWidth> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitPersona Class="session-persona" + PrimaryText="@(session.Device ?? Localizer[nameof(AppStrings.UnknownDevice)])" + SecondaryText="@($"{session.Address} ({session.IP}) - {GetLastSeenOn(session.RenewedOn)}")" + Size="BitPersonaSize.Size48" + Presence="@GetPresence(session.RenewedOn)" + Styles="@(new() { Image = "width:50%;height:50%" })" + ImageInitials="@(session.IsValid ? "✓" : "✘")" + ImageUrl="@($"/_content/Boilerplate.Client.Core/images/os/{GetImageUrl(session.Device)}")" /> + <BitSpacer /> + <BitButton Variant="BitVariant.Text" + OnClick="() => RevokeSession(session)" + IconName="@(isWaiting ? BitIconName.CloudUpload : BitIconName.Delete)" /> + </BitStack> + </BitCard> + </RowTemplate> + </BitBasicList> + } + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs similarity index 51% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs index 55dae80c3a..9796143ca9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs @@ -1,18 +1,14 @@ using Boilerplate.Shared.Controllers.Identity; using Boilerplate.Shared.Dtos.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; -public partial class UserSessionsSection +public partial class SessionsSection { - private bool isLoading; private bool isWaiting; - private string? message; - private string? currentSessionId; + private Guid? currentSessionId; private UserSessionDto? currentSession; - private ElementReference messageRef = default!; - private BitColor messageColor = BitColor.Error; - private IEnumerable<UserSessionDto> otherSessions = []; + private UserSessionDto[] otherSessions = []; [AutoInject] private IUserController userController = default!; @@ -24,9 +20,9 @@ protected override async Task OnInitAsync() await base.OnInitAsync(); } + private async Task LoadSessions() { - isLoading = true; List<UserSessionDto> userSessions = []; currentSessionId = await PrerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.GetSessionId()); @@ -36,45 +32,41 @@ private async Task LoadSessions() } finally { - isLoading = false; - otherSessions = userSessions.Where(s => s.SessionUniqueId.ToString() != currentSessionId); - currentSession = userSessions.SingleOrDefault(s => s.SessionUniqueId.ToString() == currentSessionId); + otherSessions = userSessions.Where(s => s.SessionUniqueId != currentSessionId).ToArray(); + currentSession = userSessions.SingleOrDefault(s => s.SessionUniqueId == currentSessionId); } } private async Task RevokeSession(UserSessionDto session) { - if (isWaiting || session.SessionUniqueId.ToString() == currentSessionId) return; + if (isWaiting || session.SessionUniqueId == currentSessionId) return; isWaiting = true; - message = null; try { await userController.RevokeSession(session.SessionUniqueId, CurrentCancellationToken); - message = Localizer[nameof(AppStrings.RemoveSessionSuccessMessage)]; - messageColor = BitColor.Success; - + SnackBarService.Success(Localizer[nameof(AppStrings.RemoveSessionSuccessMessage)]); await LoadSessions(); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; + SnackBarService.Error(e.Message); } finally { isWaiting = false; - await messageRef.ScrollIntoView(); } } private static string GetImageUrl(string? device) { - var d = device?.ToLower() ?? ""; + if (string.IsNullOrEmpty(device)) return "unknown.png"; + + var d = device.ToLowerInvariant(); - if (d.Contains("windows")) return "windows.png"; + if (d.Contains("win") /*Windows, WinUI, Win32*/) return "windows.png"; if (d.Contains("android")) return "android.png"; @@ -83,10 +75,17 @@ private static string GetImageUrl(string? device) return "apple.png"; } - private static BitPersonaPresence GetPresence(string? lastSeenOn) + private BitPersonaPresence GetPresence(DateTimeOffset renewedOn) + { + return DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(5) ? BitPersonaPresence.Online + : DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(15) ? BitPersonaPresence.Away + : BitPersonaPresence.Offline; + } + + private string GetLastSeenOn(DateTimeOffset renewedOn) { - return lastSeenOn == AppStrings.Online ? BitPersonaPresence.Online - : lastSeenOn == AppStrings.Recently ? BitPersonaPresence.Away - : BitPersonaPresence.Offline; + return DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(5) ? Localizer[nameof(AppStrings.Online)] + : DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(15) ? Localizer[nameof(AppStrings.Recently)] + : renewedOn.ToLocalTime().ToString("g"); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.scss new file mode 100644 index 0000000000..ed0092052f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.scss @@ -0,0 +1,18 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: 100%; +} + +::deep { + .session-persona { + @include lt-sm { + max-width: calc(100% - 3.75rem); + } + } + + .sessions-list { + width: 100%; + height: 20rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor new file mode 100644 index 0000000000..317b02acfe --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor @@ -0,0 +1,54 @@ +@*+:cnd:noEmit*@ +@attribute [Route(Urls.SettingsPage + "/{Section?}")] +@attribute [Route("{culture?}" + Urls.SettingsPage + "/{Section?}")] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.SettingsPageTitle)]</PageTitle> + +@{ + var imageUrl = user?.ProfileImageName is null ? null : $"{profileImageUrl}&file={user.ProfileImageName}"; +} + +<section> + <BitStack Class="stack"> + <Accordion Name="@Urls.SettingsSections.Profile" @bind-Value="@openedAccordion" + Title="@Localizer[nameof(AppStrings.ProfileTitle)]" + Subtitle="@Localizer[nameof(AppStrings.ProfileSubtitle)]"> + <ProfileSection Loading="isLoading" User="user" /> + </Accordion> + + <Accordion Name="@Urls.SettingsSections.Account" @bind-Value="@openedAccordion" + Title="@Localizer[nameof(AppStrings.AccountTitle)]" + Subtitle="@Localizer[nameof(AppStrings.AccountSubtitle)]"> + <BitPivot Alignment="BitAlignment.Center"> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]"> + <ChangeEmailSection Email="@user?.Email" /> + </BitPivotItem> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Phone)]"> + <ChangePhoneNumberSection PhoneNumber="@user?.PhoneNumber" /> + </BitPivotItem> + <BitPivotItem> + <Header> + <BitText Color="BitColor.Error">@Localizer[nameof(AppStrings.Delete)]</BitText> + </Header> + <Body> + <DeleteAccountSection /> + </Body> + </BitPivotItem> + </BitPivot> + </Accordion> + + <Accordion Name="@Urls.SettingsSections.Tfa" @bind-Value="@openedAccordion" + Title="@Localizer[nameof(AppStrings.TfaTitle)]" + Subtitle="@Localizer[nameof(AppStrings.TfaSubtitle)]"> + <TwoFactorSection /> + </Accordion> + + <Accordion Name="@Urls.SettingsSections.Sessions" @bind-Value="@openedAccordion" + Title="@Localizer[nameof(AppStrings.SessionsTitle)]" + Subtitle="@Localizer[nameof(AppStrings.SessionsSubtitle)]"> + <SessionsSection /> + </Accordion> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs new file mode 100644 index 0000000000..4f9869aa37 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs @@ -0,0 +1,46 @@ +//+:cnd:noEmit +using Boilerplate.Shared.Dtos.Identity; +using Boilerplate.Shared.Controllers.Identity; + +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; + +public partial class SettingsPage +{ + protected override string? Title => Localizer[nameof(AppStrings.Settings)]; + protected override string? Subtitle => string.Empty; + + + [Parameter] public string? Section { get; set; } + + + [AutoInject] private IUserController userController = default!; + + + private UserDto? user; + private bool isLoading; + private string? profileImageUrl; + private string? openedAccordion; + + + protected override async Task OnInitAsync() + { + openedAccordion = Section?.ToLower(); + + isLoading = true; + + try + { + user = await userController.GetCurrentUser(CurrentCancellationToken); + + var serverAddress = Configuration.GetServerAddress(); + var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); + profileImageUrl = $"{serverAddress}/api/Attachment/GetProfileImage?access_token={access_token}"; + } + finally + { + isLoading = false; + } + + await base.OnInitAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.scss new file mode 100644 index 0000000000..f7642812f0 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.scss @@ -0,0 +1,11 @@ +section { + width: 100%; + display: flex; + justify-content: center; +} + +::deep { + .stack { + max-width: 40rem; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor new file mode 100644 index 0000000000..4342b07bd7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor @@ -0,0 +1,152 @@ +@inherits AppComponentBase + +<section> + <BitStack> + @if (isTwoFactorAuthEnabled is false) + { + <BitText>@Localizer[nameof(AppStrings.TfaConfigureAutAppTitle)]</BitText> + + <BitText Typography="BitTypography.Body1" Color="BitColor.SecondaryForeground"> + @Localizer[nameof(AppStrings.TfaConfigureAutAppSubtitle)] + </BitText> + + <ol style="padding-inline-start: 1rem;"> + <li> + @(new MarkupString(Localizer[nameof(AppStrings.TfaConfigureAutAppStep1), + "<a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en\">Android</a>", + "<a href=\"https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8\">iOS</a>"])) + </li> + <br /> + <li> + <BitStack FillContent> + @Localizer[nameof(AppStrings.TfaConfigureAutAppStep2)] + <a href="@authenticatorUri"> + <img src="data:image/png;base64,@qrCode" style="width:256px" /> + </a> + <BitCard FullWidth> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.Button">@sharedKey</BitText> + <BitSpacer /> + <BitButton IconOnly + Variant="BitVariant.Text" + OnClick="WrapHandled(CopySharedKeyToClipboard)" + IconName="@(isKeyCopiedShown ? BitIconName.Clipboard : BitIconName.Copy)" + Title="@Localizer[isKeyCopiedShown ? nameof(AppStrings.Copied) : nameof(AppStrings.Copy)]" /> + </BitStack> + </BitCard> + </BitStack> + </li> + <br /> + <li> + @Localizer[nameof(AppStrings.TfaConfigureAutAppStep3)] + <br /> + <br /> + <div> + <BitTextField @bind-Value="verificationCode" + Type="BitInputType.Number" + AutoComplete="@BitAutoCompleteValue.Off" + Label="@Localizer[nameof(AppStrings.TfaConfigureAutAppVerificationCodeLabel)]" + Placeholder="@Localizer[nameof(AppStrings.TfaConfigureAutAppVerificationCodePlaceholder)]" /> + <br /> + <BitButton OnClick="WrapHandled(EnableTwoFactorAuth)"> + @Localizer[nameof(AppStrings.TfaConfigureAutAppVerifyButtonText)] + </BitButton> + </div> + </li> + </ol> + } + else + { + <BitPivot Alignment="BitAlignment.Center"> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaRecoveryCodesHeader)]"> + @if (recoveryCodesLeft == 0) + { + <BitMessage Color="BitColor.Error" Multiline Style="margin-bottom:1rem"> + <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesZeroLeftTitle)]</strong> + <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesZeroLeftSubtitle)]</p> + </BitMessage> + } + else if (recoveryCodesLeft == 1) + { + <BitMessage Color="BitColor.SevereWarning" Multiline Style="margin-bottom:1rem"> + <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesOneLeftTitle)]</strong> + <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesOneLeftSubtitle)]</p> + </BitMessage> + } + else if (recoveryCodesLeft <= 3) + { + <BitMessage Color="BitColor.Warning" Multiline Style="margin-bottom:1rem"> + <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesThreeLeftTitle), recoveryCodesLeft]</strong> + <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesThreeLeftSubtitle)]</p> + </BitMessage> + } + + @if (recoveryCodes is null) + { + <BitMessage Color="BitColor.Warning" Multiline Style="margin-bottom:1rem"> + @Localizer[nameof(AppStrings.TfaRecoveryCodesGenerateWraning)] + </BitMessage> + + <BitButton OnClick="WrapHandled(GenerateRecoveryCode)"> + @Localizer[nameof(AppStrings.TfaRecoveryCodesGenerateButtonText)] + </BitButton> + } + else + { + <BitStack> + <BitMessage Color="BitColor.Warning" Multiline> + <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesWarningTitle)]</strong> + <br /> + <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesWarning)]</p> + </BitMessage> + + <BitCard FullWidth> + <BitStack> + <BitStack Horizontal> + <BitText><b>@Localizer[nameof(AppStrings.TfaRecoveryCodesTitle)]</b></BitText> + <BitSpacer /> + <BitButton IconOnly + Variant="BitVariant.Text" + OnClick="WrapHandled(CopyRecoveryCodesToClipboard)" + IconName="@(isCodesCopiedShown ? BitIconName.Clipboard : BitIconName.Copy)" + Title="@Localizer[isCodesCopiedShown ? nameof(AppStrings.Copied) : nameof(AppStrings.Copy)]" /> + </BitStack> + @foreach (var recoveryCode in recoveryCodes) + { + <BitText Typography="BitTypography.Button">@recoveryCode</BitText> + } + </BitStack> + </BitCard> + </BitStack> + } + </BitPivotItem> + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaAuthAppHeader)]"> + <BitStack> + <BitMessage Color="BitColor.Warning" Multiline> + <b>@Localizer[nameof(AppStrings.TfaAuthAppWarningTitle)]</b> + <p>@Localizer[nameof(AppStrings.TfaAuthAppWarning)]</p> + </BitMessage> + + <BitButton OnClick="WrapHandled(ResetAuthenticatorKey)"> + @Localizer[nameof(AppStrings.TfaAuthAppResetKeyButtonText)] + </BitButton> + </BitStack> + </BitPivotItem> + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaDisable2faHeader)]"> + <BitStack> + <BitMessage Color="BitColor.Warning" Multiline> + <strong>@Localizer[nameof(AppStrings.TfaDisable2faWarningTitle)]</strong> + <p>@Localizer[nameof(AppStrings.TfaDisable2faWarning)]</p> + </BitMessage> + + <BitButton OnClick="WrapHandled(DisableTwoFactorAuth)"> + @Localizer[nameof(AppStrings.TfaDisable2faButtonText)] + </BitButton> + </BitStack> + </BitPivotItem> + </BitPivot> + } + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.cs similarity index 91% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.cs index 1f58f6fa6c..c73d2f9386 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.cs @@ -1,7 +1,7 @@ using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Shared.Controllers.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Settings; public partial class TwoFactorSection { @@ -16,9 +16,6 @@ public partial class TwoFactorSection private string? verificationCode; private bool isTwoFactorAuthEnabled; - private string? message; - private ElementReference messageRef = default!; - [AutoInject] private Clipboard clipboard = default!; [AutoInject] private IUserController userController = default!; @@ -42,8 +39,6 @@ private async Task EnableTwoFactorAuth() var response = await SendTwoFactorAuthRequest(request); recoveryCodes = response?.RecoveryCodes; - - await messageRef.ScrollIntoView(); } private async Task DisableTwoFactorAuth() @@ -76,7 +71,6 @@ private async Task ResetAuthenticatorKey() { if (isWaiting) return null; - message = null; isWaiting = true; try @@ -93,10 +87,7 @@ private async Task ResetAuthenticatorKey() } catch (KnownException e) { - message = e.Message; - - await messageRef.ScrollIntoView(); - + SnackBarService.Error(e.Message); return null; } finally diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.scss new file mode 100644 index 0000000000..3db8bb4a76 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/TwoFactorSection.razor.scss @@ -0,0 +1,6 @@ +section { + width: 100%; +} + +::deep { +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor new file mode 100644 index 0000000000..da6c6d9b4e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor @@ -0,0 +1,133 @@ +@attribute [Route(Urls.TodoPage)] +@attribute [Route("{culture?}" + Urls.TodoPage)] +@attribute [Authorize] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.TodoPageTitle)]</PageTitle> + +<section> + <BitStack Class="stack"> + <BitStack Horizontal AutoHeight> + <BitTextField @ref="newTodoInput" + Style="flex-grow:1" + @bind-Value="newTodoTitle" + Immediate DebounceTime="300" + Placeholder="@Localizer[nameof(AppStrings.TodoAddPlaceholder)]" + OnKeyDown="WrapHandled(async (KeyboardEventArgs args) => await OnInputKeyDown(args))" /> + + <BitButton AutoLoading + OnClick="WrapHandled(AddTodoItem)" + Title="@Localizer[nameof(AppStrings.Add)]" + IsEnabled="(string.IsNullOrWhiteSpace(newTodoTitle) is false)"> + @Localizer[nameof(AppStrings.Add)] + </BitButton> + </BitStack> + + <BitStack Gap="0.25rem"> + <BitStack Horizontal VerticalAlign="BitAlignment.End" Gap="0.25rem" AutoHeight Class="todo-header"> + <BitPivot SelectedKey="@selectedFilter" SelectedKeyChanged="FilterTodoItems" HeaderOnly> + <BitPivotItem Key="@nameof(AppStrings.All)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.All)]" /> + <BitPivotItem Key="@nameof(AppStrings.Active)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.Active)]" /> + <BitPivotItem Key="@nameof(AppStrings.Completed)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.Completed)]" /> + </BitPivot> + <BitSpacer /> + <BitSearchBox @ref="searchBox" + Class="todo-search" + OnChange="SearchTodoItems" + Immediate DebounceTime="300" + OnClear="@(() => SearchTodoItems(""))" + Placeholder="@Localizer[nameof(AppStrings.TodoSearchPlaceholder)]" + Styles="@(new() { InputContainer="border:none;background-color:var(--bit-clr-bg-sec)" })" /> + <BitStack Class="todo-sort" Gap="2px" Horizontal AutoSize> + <BitDropdown Responsive NoBorder FitWidth + DefaultValue="nameof(AppStrings.Alphabetical)" + TItem="BitDropdownOption<string>" TValue="string" + Classes="@(new() { Callout="todo-sort-callout" })" + OnSelectItem="(BitDropdownOption<string> item) => SortTodoItems(item.Value)" + Styles="@(new() { Container="height:32px;background-color:var(--bit-clr-bg-sec)" })"> + <Options> + <BitDropdownOption Text="@Localizer[nameof(AppStrings.Alphabetical)]" Value="nameof(AppStrings.Alphabetical)" /> + <BitDropdownOption Text="@Localizer[nameof(AppStrings.Date)]" Value="nameof(AppStrings.Date)" /> + </Options> + <CaretDownTemplate> + <BitIcon IconName="@BitIconName.Breadcrumb" /> + </CaretDownTemplate> + </BitDropdown> + <BitButton IconOnly Color="BitColor.SecondaryBackground" Style="height:32px;" + OnClick="() => { isDescendingSort = !isDescendingSort; FilterViewTodoItems(); }" + IconName="@(isDescendingSort ? BitIconName.SortDown : BitIconName.SortUp)" /> + </BitStack> + </BitStack> + + @if (isLoading) + { + <BitStack Alignment="BitAlignment.Center"> + <BitRingLoading /> + </BitStack> + } + else + { + <BitBasicList Items="viewTodoItems" EnableVirtualization + Class="todo-list"> + <EmptyContent> + <BitStack Alignment="BitAlignment.Center" Style="padding:1rem;"> + <BitImage Src="/_content/Boilerplate.Client.Core/images/backgrounds/empty-todo-list-bg.svg" /> + <BitLabel>@Localizer[nameof(AppStrings.NoTodos)]</BitLabel> + </BitStack> + </EmptyContent> + <RowTemplate Context="todo"> + <BitStack Alignment="BitAlignment.Center" Style="padding:1rem 1rem 0" AutoHeight> + <BitStack Horizontal @key=@todo.Id VerticalAlign="BitAlignment.Center"> + @if (todo.IsInEditMode is false) + { + <BitStack Grows Gap="0"> + <BitCheckbox Label="@todo.Title" + Styles="@(new() { Label = todo.IsDone ? "text-decoration:line-through" : "" })" + DefaultValue="todo.IsDone" + OnChange="() => ToggleIsDone(todo)" /> + <BitText Typography="BitTypography.Body2">@todo.Date.ToLocalTime().ToString("F")</BitText> + </BitStack> + + <BitStack Horizontal AutoWidth Gap="0.5rem"> + <BitButton Variant="BitVariant.Text" + IconName="@BitIconName.Edit" + Color="BitColor.SecondaryForeground" + Title="@Localizer[nameof(AppStrings.Edit)]" + OnClick="WrapHandled(() => ToggleEditMode(todo))" /> + + <BitButton Color="BitColor.Error" + Variant="BitVariant.Text" + IconName="@BitIconName.Delete" + Title="@Localizer[nameof(AppStrings.Remove)]" + OnClick="WrapHandled(() => { isDeleteDialogOpen = true; deletingTodoItem = todo; })" /> + </BitStack> + } + else + { + <BitTextField Style="flex-grow:1" @bind-Value="underEditTodoItemTitle" /> + + <BitButton Color="BitColor.TertiaryBackground" + Title="@Localizer[nameof(AppStrings.Cancel)]" + OnClick="WrapHandled(() => ToggleEditMode(todo))"> + @Localizer[nameof(AppStrings.Cancel)] + </BitButton> + <BitButton Title="@Localizer[nameof(AppStrings.Edit)]" OnClick="WrapHandled(() => SaveTodoItem(todo))"> + @Localizer[nameof(AppStrings.Save)] + </BitButton> + } + </BitStack> + <BitSeparator Border="BitColorKind.Tertiary" /> + </BitStack> + </RowTemplate> + </BitBasicList> + } + </BitStack> + </BitStack> +</section> + +<BitDialog OnOk="WrapHandled(DeleteTodoItem)" + @bind-IsOpen="isDeleteDialogOpen" + OkText="@Localizer[nameof(AppStrings.Yes)]" + CancelText="@Localizer[nameof(AppStrings.No)]" + Title="@Localizer[nameof(AppStrings.DeleteTodoItem)]" + Message="@Localizer.GetString(nameof(AppStrings.AreYouSureWannaDelete), deletingTodoItem?.Title ?? "")" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.cs similarity index 61% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.cs index 5928e7ad37..a9c3f06851 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.cs @@ -1,26 +1,30 @@ using Boilerplate.Shared.Controllers.Todo; using Boilerplate.Shared.Dtos.Todo; +using Microsoft.AspNetCore.Components.Web; -namespace Boilerplate.Client.Core.Components.Pages.Todo; +namespace Boilerplate.Client.Core.Components.Pages.Authorized.Todo; -[Authorize] public partial class TodoPage { + protected override string? Title => Localizer[nameof(AppStrings.Todo)]; + protected override string? Subtitle => string.Empty; + [AutoInject] Keyboard keyboard = default!; [AutoInject] ITodoItemController todoItemController = default!; - private bool isAdding; private bool isLoading; private string? searchText; private string? selectedSort; + private bool isDescendingSort; private string? selectedFilter; + private bool isDeleteDialogOpen; + private TodoItemDto? deletingTodoItem; private string? underEditTodoItemTitle; - private string newTodoTitle = string.Empty; - private ConfirmMessageBox confirmMessageBox = default!; - private IList<TodoItemDto> allTodoItems = []; - private IList<TodoItemDto> viewTodoItems = default!; - private List<BitDropdownItem<string>> sortItems = []; private BitSearchBox searchBox = default!; + private string newTodoTitle = string.Empty; + private List<TodoItemDto> allTodoItems = []; + private List<TodoItemDto> viewTodoItems = []; + private BitTextField newTodoInput = default!; protected override async Task OnInitAsync() { @@ -29,12 +33,6 @@ protected override async Task OnInitAsync() selectedFilter = nameof(AppStrings.All); selectedSort = nameof(AppStrings.Alphabetical); - sortItems = - [ - new BitDropdownItem<string> { Text = Localizer[nameof(AppStrings.Alphabetical)], Value = nameof(AppStrings.Alphabetical) }, - new BitDropdownItem<string> { Text = Localizer[nameof(AppStrings.Date)], Value = nameof(AppStrings.Date) } - ]; - await LoadTodoItems(); await base.OnInitAsync(); @@ -58,11 +56,18 @@ private async Task LoadTodoItems() private void FilterViewTodoItems() { - viewTodoItems = allTodoItems - .Where(t => TodoItemIsVisible(t)) - .OrderByIf(selectedSort == nameof(AppStrings.Alphabetical), t => t.Title!) - .OrderByIf(selectedSort == nameof(AppStrings.Date), t => t.Date!) - .ToList(); + var items = allTodoItems.Where(TodoItemIsVisible); + if (isDescendingSort) + { + items = items.OrderByDescendingIf(selectedSort == nameof(AppStrings.Alphabetical), t => t.Title!) + .OrderByDescendingIf(selectedSort == nameof(AppStrings.Date), t => t.Date!); + } + else + { + items = items.OrderByIf(selectedSort == nameof(AppStrings.Alphabetical), t => t.Title!) + .OrderByIf(selectedSort == nameof(AppStrings.Date), t => t.Date!); + } + viewTodoItems = items.ToList(); } private bool TodoItemIsVisible(TodoItemDto todoItem) @@ -90,9 +95,9 @@ private void SearchTodoItems(string text) FilterViewTodoItems(); } - private void SortTodoItems(BitDropdownItem<string> sort) + private void SortTodoItems(string? sort) { - selectedSort = sort.Value; + selectedSort = sort; FilterViewTodoItems(); } @@ -112,50 +117,41 @@ private void ToggleEditMode(TodoItemDto todoItem) private async Task AddTodoItem() { - if (isAdding) return; + if (string.IsNullOrWhiteSpace(newTodoTitle)) return; - isAdding = true; + var addedTodoItem = await todoItemController.Create(new() { Title = newTodoTitle }, CurrentCancellationToken); - try - { - var addedTodoItem = await todoItemController.Create(new() { Title = newTodoTitle }, CurrentCancellationToken); + allTodoItems.Add(addedTodoItem!); - allTodoItems.Add(addedTodoItem!); + if (TodoItemIsVisible(addedTodoItem!)) + { + viewTodoItems.Add(addedTodoItem!); + } - if (TodoItemIsVisible(addedTodoItem!)) - { - viewTodoItems.Add(addedTodoItem!); - } + newTodoTitle = ""; + await newTodoInput.FocusAsync(); + } - newTodoTitle = ""; - } - finally + private async Task OnInputKeyDown(KeyboardEventArgs args) + { + if (args.Key == "Enter") { - isAdding = false; + await AddTodoItem(); } } - private async Task DeleteTodoItem(TodoItemDto todoItem) + private async Task DeleteTodoItem() { - if (isLoading) return; + if (isLoading || deletingTodoItem is null) return; + + isLoading = true; try { - var confirmed = await confirmMessageBox.Show(Localizer.GetString(nameof(AppStrings.AreYouSureWannaDelete), todoItem.Title!), - Localizer[nameof(AppStrings.DeleteTodoItem)]); - - if (confirmed) - { - isLoading = true; - - StateHasChanged(); - - await todoItemController.Delete(todoItem.Id, CurrentCancellationToken); - - allTodoItems.Remove(todoItem); + await todoItemController.Delete(deletingTodoItem.Id, CurrentCancellationToken); - viewTodoItems.Remove(todoItem); - } + allTodoItems.Remove(deletingTodoItem); + viewTodoItems.Remove(deletingTodoItem); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.scss new file mode 100644 index 0000000000..b22d8d1fb4 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor.scss @@ -0,0 +1,63 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; +@import '../../../../Styles/abstracts/_bit-css-variables.scss'; + +section { + width: 100%; + height: 100%; + display: flex; + justify-content: center; +} + +::deep { + .stack { + max-width: 45rem; + } + + .todo-header { + @include lt-sm { + gap: 0 !important; + position: relative; + align-items: flex-start !important; + flex-direction: column-reverse !important; + } + } + + .todo-search { + width: 192px; + + @include lt-sm { + width: 100%; + } + } + + .todo-sort { + @include lt-sm { + bottom: 0; + position: absolute; + inset-inline-end: 0; + } + } + + .todo-sort-callout { + @include lt-sm { + height: unset; + box-shadow: none; + bottom: var(--app-inset-bottom); + top: var(--app-inset-top) !important; + } + } + + .todo-list { + width: 100%; + background-color: var(--bit-clr-bg-sec); + height: calc(var(--app-height) - 14rem); + + @include lt-md { + height: calc(var(--app-height) - 17rem); + } + + @include lt-sm { + height: calc(var(--app-height) - 18rem); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/_Imports.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/_Imports.razor new file mode 100644 index 0000000000..45636cc948 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/_Imports.razor @@ -0,0 +1,5 @@ +@*+:cnd:noEmit*@ +@*#if (sample == "Admin")*@ +@using Boilerplate.Shared.Dtos.Categories +@using Boilerplate.Shared.Dtos.Products +@*#endif*@ \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor deleted file mode 100644 index 2413c0b8a0..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor +++ /dev/null @@ -1,96 +0,0 @@ -@attribute [Route(Urls.AddOrEditCategoryPage + "/{Id:guid?}")] -@attribute [Route("{culture?}" + Urls.AddOrEditCategoryPage + "/{Id:guid?}")] -@inherits AppComponentBase - -<PageTitle> - @if (category.Id == default) - { - @Localizer[nameof(AppStrings.AddCategory)] - } - else - { - @Localizer[nameof(AppStrings.EditCategory)] - } -</PageTitle> - -<div class="page-container"> - <div class="page-header"> - <BitButton Class="back-btn" - Variant="BitVariant.Text" - Href="@Urls.CategoriesPage" - IconName="@BitIconName.Back" - Title="@Localizer[nameof(AppStrings.Back)]" /> - <h1 class="page-title"> - @if (category.Id == default) - { - @Localizer[nameof(AppStrings.AddCategory)] - } - else - { - @Localizer[nameof(AppStrings.EditCategory)] - } - </h1> - </div> - <EditForm Model="category" OnValidSubmit="WrapHandled(Save)"> - <AppDataAnnotationsValidator /> - - @if (string.IsNullOrEmpty(saveMessage) is false) - { - <BitMessage Color="@saveMessageColor" OnDismiss="() => saveMessage = null">@saveMessage</BitMessage> - } - - @if (isLoading) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - <div class="form-input-container"> - <BitTextField @bind-Value="category.Name" - Label="@Localizer[nameof(AppStrings.Name)]" - Placeholder="@Localizer[nameof(AppStrings.EnterCategoryName)]" /> - <ValidationMessage For="() => category.Name" /> - </div> - - <div class="form-input-container"> - <BitLabel For="catColorInput">@Localizer[nameof(AppStrings.Color)]</BitLabel> - <BitStack> - <BitStack Horizontal> - @foreach (var color in new[] { "#FFCD56", "#FF6384", "#4BC0C0", "#FF9124", "#2B88D8", "#C7E0F4" }) - { - <button @onclick="() => SetCategoryColor(color)" - class="color-btn @(category.Color == color ? "color-btn--active" : null)" - style="background-color: @color" - type="button" /> - } - </BitStack> - <div /> - <BitStack Horizontal> - <div class="color-square selected" style="background-color: @category.Color"></div> - <BitToggleButton @bind-bind-IsChecked="isColorPickerOpen" - Variant="BitVariant.Outline" - OnClick=@ToggleColorPicker> - @Localizer[(nameof(AppStrings.CustomColor))] - </BitToggleButton> - </BitStack> - @if (isColorPickerOpen) - { - <div class="color-picker-container"> - <BitColorPicker @bind-Color="category.Color" Id="catColorInput" ShowPreview="true"> - @Localizer[nameof(AppStrings.DefaultColorPicker)] - </BitColorPicker> - </div> - } - </BitStack> - <ValidationMessage For="() => category.Color" /> - </div> - - <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.Save)] - </BitButton> - } - </EditForm> -</div> - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.scss deleted file mode 100644 index 24a74bf750..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/AddOrEditCategoryPage.razor.scss +++ /dev/null @@ -1,87 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - flex-grow: 1; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.page-header { - display: flex; - align-items: center; - margin-bottom: rem2(37px); - - @include sm { - margin-bottom: rem2(29px); - } -} - -::deep .back-btn { - margin-inline-end: rem2(12px); - - &[dir=rtl] { - transform: scale(-1); - } -} - -.page-title { - margin: 0; - font-weight: 700; - font-size: rem2(24px); - line-height: rem2(32px); - - @include md { - font-weight: 600; - font-size: rem2(20px); - line-height: rem2(28px); - } - - @include sm { - font-weight: 600; - font-size: rem2(18px); - line-height: rem2(24px); - } -} - -.form-input-container { - width: 100%; - min-width: rem2(270px); - margin-bottom: rem2(24px); -} - -.color-btn { - border: none; - outline: none; - cursor: pointer; - width: rem2(32px); - height: rem2(32px); - border-radius: rem2(2px); -} - -.color-btn--active { - background-size: rem2(20px); - background-position: center; - background-repeat: no-repeat; - background-image: url('images/icons/checkmark-icon.svg'); -} - -.color-square { - width: rem2(32px); - height: rem2(32px); - display: inline-block; - border-radius: rem2(2px); - - &.selected { - width: rem2(80px); - } -} - -::deep .validation-message { - color: $bit-color-error; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor deleted file mode 100644 index 0bd512c18c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor +++ /dev/null @@ -1,55 +0,0 @@ -@attribute [Route(Urls.CategoriesPage)] -@attribute [Route("{culture?}" + Urls.CategoriesPage)] -@using Boilerplate.Shared.Dtos.Categories -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.CategoriesPageTitle)]</PageTitle> - -<div class="page-container"> - <div class="page-row"> - <h1 class="page-title">@Localizer[nameof(AppStrings.Categories)]</h1> - <BitButton OnClick="CreateCategory">@Localizer[nameof(AppStrings.AddCategory)]</BitButton> - </div> - - <div class="grid"> - <div class="grid-container"> - <BitDataGrid @ref="dataGrid" ItemsProvider="categoriesProvider" TGridItem="CategoryDto" ResizableColumns="true" Pagination="pagination"> - <BitDataGridPropertyColumn Sortable="true" Property="c => c!.Name" Title="@Localizer[nameof(AppStrings.Name)]" Align="BitDataGridAlign.Left"> - <ColumnOptions> - <BitStack Horizontal> - <BitSearchBox @bind-Value="CategoryNameFilter" - Immediate DebounceTime="500" - Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" - InputHtmlAttributes="@(new Dictionary<string, object> {{"autofocus", true}})" /> - @if (isLoading) - { - <div class="loading-container"> - <BitEllipsisLoading CustomSize="32" /> - </div> - } - </BitStack> - </ColumnOptions> - </BitDataGridPropertyColumn> - <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.ProductsCount)]" Property="p => p!.ProductsCount" Sortable="true" Class="id-col" - Align="BitDataGridAlign.Left" IsDefaultSort="BitDataGridSortDirection.Ascending" /> - <BitDataGridTemplateColumn Title="@Localizer[nameof(AppStrings.Color)]" Align="BitDataGridAlign.Left" Context="category"> - <span class="color-box" style="background-color:@(category!.Color)"></span> - </BitDataGridTemplateColumn> - <BitDataGridTemplateColumn Title="@Localizer[nameof(AppStrings.Action)]" Align="BitDataGridAlign.Center" Context="category"> - <BitButton IconName="@BitIconName.Edit" - Variant="BitVariant.Text" - Title="@Localizer[(nameof(AppStrings.Edit))]" - OnClick="() => EditCategory(category!)" /> - <BitButton Color="BitColor.Error" - Variant="BitVariant.Text" - IconName="@BitIconName.Delete" - Title="@Localizer[(nameof(AppStrings.Delete))]" - OnClick="WrapHandled(() => DeleteCategory(category!))" /> - </BitDataGridTemplateColumn> - </BitDataGrid> - </div> - <BitDataGridPaginator Value="pagination" /> - </div> -</div> - -<ConfirmMessageBox @ref=confirmMessageBox /> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.scss deleted file mode 100644 index 7406716ec0..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.scss +++ /dev/null @@ -1,138 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - flex-grow: 1; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.page-row { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - margin-bottom: rem2(24px); - justify-content: space-between; -} - -.page-title { - margin: 0; - font-weight: 600; - font-size: rem2(20px); - line-height: rem2(28px); - - @include sm { - font-size: rem2(18px); - line-height: rem2(24px); - } -} - -.grid-container { - overflow: auto; -} - -.grid { - width: 100%; - display: inline-flex; - flex-direction: column; - border-radius: rem2(2px); - border: rem2(1px) solid $bit-color-border-secondary; - - ::deep { - .id-col { - width: 90px; - } - - .bitdatagrid th .col-options-button { - cursor: help; - } - } -} - -::deep table { - min-width: 100%; - border-spacing: 0; -} - -::deep thead { - height: rem2(43px); - background-color: $bit-color-background-primary; -} - -::deep tr { - height: rem2(43px); -} - -::deep th { - font-weight: 600; - padding: 0 rem2(12px); - font-size: rem2(14px); - line-height: rem2(20px); -} - -::deep td { - font-weight: 400; - white-space: nowrap; - font-size: rem2(12px); - line-height: rem2(16px); - border-top: rem2(1px) solid $bit-color-border-secondary; -} - -::deep tr td:first-child { - font-size: rem2(14px); - line-height: rem2(20px); - padding-inline-start: rem2(12px); -} - -::deep .col-options-button { - &:hover { - background-color: transparent; - } -} - -::deep .col-title { - &:hover { - background-color: transparent !important; - } -} - -::deep .category-search-box { - width: 100%; - max-width: rem2(300px); - margin-bottom: rem2(24px); -} - -::deep th:nth-child(2) .col-options-button { - cursor: pointer; - background-image: none; - - &:before { - content: "\E721"; - font-style: normal; - font-weight: normal; - display: inline-block; - font-family: 'Fabric MDL2'; - } -} - -::deep .bitdatagrid-paginator { - margin-top: 0; - overflow: auto; - padding: 0 rem2(12px); - font-size: rem2(14px); - min-height: rem2(43px); - border-top: rem2(1px) solid $bit-color-border-secondary; -} - -.color-box { - display: block; - width: rem2(24px); - height: rem2(24px); - border-radius: rem2(2px); -} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor deleted file mode 100644 index 0d0248ebc4..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor +++ /dev/null @@ -1,23 +0,0 @@ -@attribute [Route(Urls.DashboardPage)] -@attribute [Route("{culture?}" + Urls.DashboardPage)] -@inherits AppComponentBase - -@if (isLoadingAssemblies) -{ - <BitRingLoading /> -} -else -{ - <div class="page-container"> - <OverallStatsWidget /> - - <div class="page-row"> - <ProductsCountPerCategoryWidget /> - <ProductsPercentageWidget /> - </div> - - <div class="page-row"> - <ProductsSalesWidget /> - </div> - </div> -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.cs deleted file mode 100644 index 05e68e0028..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.cs +++ /dev/null @@ -1,36 +0,0 @@ -//+:cnd:noEmit -using Microsoft.AspNetCore.Components.WebAssembly.Services; - -namespace Boilerplate.Client.Core.Components.Pages.Dashboard; - -[Authorize] -public partial class DashboardPage -{ - [AutoInject] LazyAssemblyLoader lazyAssemblyLoader = default!; - - private bool isLoadingAssemblies = true; - - protected async override Task OnInitAsync() - { - try - { - if (AppPlatform.IsBrowser) - { - await lazyAssemblyLoader.LoadAssembliesAsync([ - //#if (sample == "Admin" && offlineDb == false) - "System.Private.Xml.wasm", "System.Data.Common.wasm", - //#endif - //#if (sample == "Admin") - "Newtonsoft.Json.wasm"] - //#endif - ); - } - } - finally - { - isLoadingAssemblies = false; - } - - await base.OnInitAsync(); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.scss deleted file mode 100644 index 8bd1fefbe8..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/DashboardPage.razor.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; - -.page-container { - width: 100%; - height: 100%; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.page-row { - width: 100%; - display: flex; - gap: rem2(24px); - flex-flow: row wrap; -} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor deleted file mode 100644 index 6b6b19f0d0..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor +++ /dev/null @@ -1,56 +0,0 @@ -@inherits AppComponentBase - -<div class="card-container"> - <div class="card"> - <div class="card-num @(isLoading ? "no-border" : null)"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - @data.Last30DaysProductCount - } - </div> - <div class="card-title">@Localizer[nameof(AppStrings.Last30DaysProductCount)]</div> - </div> - <div class="card"> - <div class="card-num @(isLoading ? "no-border" : null)"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - @data.TotalProducts - } - </div> - <div class="card-title">@Localizer[nameof(AppStrings.TotalProducts)]</div> - </div> - <div class="card"> - <div class="card-num @(isLoading ? "no-border" : null)"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - @data.TotalCategories - } - </div> - <div class="card-title">@Localizer[nameof(AppStrings.Last30DaysCategoryCount)]</div> - </div> - <div class="card"> - <div class="card-num @(isLoading ? "no-border" : null)"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - @data.TotalCategories - } - </div> - <div class="card-title">@Localizer[nameof(AppStrings.TotalCategories)]</div> - </div> -</div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.scss deleted file mode 100644 index 37f4a5c7cc..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/OverallStatsWidget.razor.scss +++ /dev/null @@ -1,117 +0,0 @@ -@import "../../../Styles/abstracts/_functions.scss"; -@import "../../../Styles/abstracts/_media-queries.scss"; -@import "../../../Styles/abstracts/_bit-css-variables.scss"; - -.card-container { - width: 100%; - display: flex; - gap: rem2(24px); - flex-flow: row wrap; - align-items: flex-start; - margin-bottom: rem2(56px); - justify-content: space-between; - - @include md { - margin-bottom: rem2(40px); - } - - @include sm { - gap: rem2(16px); - margin-bottom: rem2(24px); - } -} - -.card { - height: 100%; - display: flex; - padding: rem2(24px); - min-height: rem2(168px); - width: calc(25% - 18px); - align-items: flex-start; - border-radius: rem2(8px); - flex-flow: column nowrap; - transition: 0.3s all linear; - justify-content: flex-start; - background-repeat: no-repeat; - background-size: rem2(84px) 100%; - background-position: center right; - background-image: url('images/stat-card-bg.svg'); - background-color: $bit-color-background-primary; - border: rem2(1px) solid $bit-color-border-secondary; - - @include sm { - height: 204px; - } - - .card-num { - font-weight: 600; - font-size: rem2(32px); - line-height: rem2(40px); - margin-bottom: rem2(24px); - color: $bit-color-primary; - - @include md { - font-size: rem2(28px); - line-height: rem2(36px); - } - - @include sm { - font-size: rem2(28px); - line-height: rem2(36px); - } - } - - .no-border { - border-bottom: none; - } - - .card-title { - font-weight: 400; - font-size: rem2(20px); - line-height: rem2(28px); - - @include md { - font-size: rem2(18px); - line-height: rem2(24px); - } - - @include sm { - font-size: rem2(16px); - line-height: rem2(22px); - } - } - - &:hover { - background-size: rem2(128px) 100%; - background-color: $bit-color-primary; - background-image: url('images/stat-card-bg-hover.svg'); - - .card-num { - color: $bit-color-primary-text; - } - - .card-title { - color: $bit-color-primary-text; - } - } - - @include md { - min-height: rem2(160px); - padding: rem2(24px) rem2(16px); - background-position: center right rem2(-3px); - - &:hover { - background-position: center right rem2(-10px); - } - } - - @include sm { - width: calc(50% - 8px); - min-height: rem2(144px); - background-position: center right rem2(-7px); - - &:hover { - background-position: center right rem2(-10px); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor deleted file mode 100644 index e10b78d77c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor +++ /dev/null @@ -1,24 +0,0 @@ -@inherits AppComponentBase - -<div class="card"> - <div class="card-header"> - <div class="title"> - @Localizer[nameof(AppStrings.ProductsCountPerCategoryChart)] - </div> - <div class="subtitle"> - @Localizer[nameof(AppStrings.ProductsCountPerCategoryChartText)] - </div> - </div> - <div class="card-body"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - <BitChart Config="config" @ref="chart" /> - } - </div> -</div> - - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.scss deleted file mode 100644 index 1c7f8b8488..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.scss +++ /dev/null @@ -1,90 +0,0 @@ -@import "../../../Styles/abstracts/_functions.scss"; -@import "../../../Styles/abstracts/_media-queries.scss"; -@import "../../../Styles/abstracts/_bit-css-variables.scss"; - -.card { - display: flex; - min-height: rem2(376px); - width: calc(50% - 12px); - align-items: flex-start; - border-radius: rem2(2px); - flex-flow: column nowrap; - margin-bottom: rem2(24px); - justify-content: flex-start; - padding: rem2(24px) rem2(16px) rem2(58px); - background-color: $bit-color-background-primary; - border: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding: rem2(24px) rem2(16px) rem2(36px); - } - - @include sm { - width: 100%; - margin-bottom: rem2(16px); - padding: rem2(24px) rem2(8px) rem2(32px); - } -} - -.card-header { - width: 100%; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - padding-bottom: rem2(16px); - justify-content: flex-start; - border-bottom: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding-bottom: rem2(12px); - } -} - -.title { - font-weight: 600; - font-size: rem2(18px); - line-height: rem2(24px); - margin-bottom: rem2(4px); - - @include md { - font-size: rem2(16px); - line-height: rem2(22px); - } - - @include sm { - font-size: rem2(14px); - line-height: rem2(20px); - } -} - -.subtitle { - font-weight: 400; - font-size: rem2(14px); - line-height: rem2(20px); - - @include md { - font-size: rem2(12px); - line-height: rem2(16px); - } - - @include sm { - font-size: rem2(10px); - line-height: rem2(12px); - } -} - -.card-body { - width: 100%; - display: flex; - align-items: flex-start; - justify-content: flex-start; - padding: rem2(32px) rem2(16px) 0; - - @include md { - padding: rem2(32px) rem2(8px) 0; - } - - @include sm { - padding: rem2(32px) rem2(8px) 0; - } -} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor deleted file mode 100644 index f72ff84729..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor +++ /dev/null @@ -1,22 +0,0 @@ -@inherits AppComponentBase - -<div class="card"> - <div class="card-header"> - <div class="title"> - @Localizer[nameof(AppStrings.ProductsPercentagePerCategory)] - </div> - <div class="subtitle"> - @Localizer[nameof(AppStrings.ProductsPercentagePerCategoryText)] - </div> - </div> - <div class="card-body"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - <BitChart Config="config" /> - } - </div> -</div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.scss deleted file mode 100644 index 20a75dddae..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsPercentageWidget.razor.scss +++ /dev/null @@ -1,90 +0,0 @@ -@import "../../../Styles/abstracts/_functions.scss"; -@import "../../../Styles/abstracts/_media-queries.scss"; -@import "../../../Styles/abstracts/_bit-css-variables.scss"; - -.card { - display: flex; - min-height: rem2(376px); - width: calc(50% - 12px); - align-items: flex-start; - border-radius: rem2(2px); - flex-flow: column nowrap; - margin-bottom: rem2(24px); - justify-content: flex-start; - padding: rem2(24px) rem2(16px) rem2(58px); - background-color: $bit-color-background-primary; - border: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding: rem2(24px) rem2(16px) rem2(36px); - } - - @include sm { - width: 100%; - margin-bottom: rem2(16px); - padding: rem2(24px) rem2(8px) rem2(32px); - } -} - -.card-header { - width: 100%; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - padding-bottom: rem2(16px); - justify-content: flex-start; - border-bottom: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding-bottom: rem2(12px); - } -} - -.title { - font-weight: 600; - font-size: rem2(18px); - line-height: rem2(24px); - margin-bottom: rem2(4px); - - @include md { - font-size: rem2(16px); - line-height: rem2(22px); - } - - @include sm { - font-size: rem2(14px); - line-height: rem2(20px); - } -} - -.subtitle { - font-weight: 400; - font-size: rem2(14px); - line-height: rem2(20px); - - @include md { - font-size: rem2(12px); - line-height: rem2(16px); - } - - @include sm { - font-size: rem2(10px); - line-height: rem2(12px); - } -} - -.card-body { - width: 100%; - display: flex; - align-items: flex-start; - justify-content: flex-start; - padding: rem2(32px) rem2(16px) 0; - - @include md { - padding: rem2(32px) rem2(8px) 0; - } - - @include sm { - padding: rem2(32px) rem2(8px) 0; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor deleted file mode 100644 index f9116f032c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor +++ /dev/null @@ -1,24 +0,0 @@ -@inherits AppComponentBase - -<div class="card"> - <div class="card-header"> - <div class="title"> - @Localizer[nameof(AppStrings.ProductSales)] - </div> - <div class="subtitle"> - @Localizer[nameof(AppStrings.ProductSalesText)] - </div> - </div> - <div class="card-body"> - @if (isLoading) - { - <BitRippleLoading /> - } - else - { - <BitChart Config="config" @ref="chart" /> - } - </div> -</div> - - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.cs deleted file mode 100644 index 0f3602d91c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Boilerplate.Shared.Controllers.Dashboard; - -namespace Boilerplate.Client.Core.Components.Pages.Dashboard; - -public partial class ProductsSalesWidget -{ - [AutoInject] IDashboardController dashboardController = default!; - - private bool isLoading; - private BitChart? chart; - private BitChartBarConfig config = default!; - - protected override async Task OnInitAsync() - { - config = new BitChartBarConfig - { - Options = new BitChartBarOptions - { - Responsive = true, - Legend = new BitChartLegend() - { - Display = false, - }, - } - }; - - await GetData(); - } - - private async Task GetData() - { - try - { - isLoading = true; - - var data = await dashboardController.GetProductsSalesStats(CurrentCancellationToken); - - BitChartBarDataset<decimal> chartDataSet = [.. data.Select(d => d.SaleAmount)]; - chartDataSet.BackgroundColor = data.Select(d => d.CategoryColor ?? string.Empty).ToArray(); - config.Data.Datasets.Add(chartDataSet); - config.Data.Labels.AddRange(data.Select(d => d.ProductName ?? string.Empty)); - } - finally - { - isLoading = false; - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.scss deleted file mode 100644 index 3ea02d1404..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Dashboard/ProductsSalesWidget.razor.scss +++ /dev/null @@ -1,90 +0,0 @@ -@import "../../../Styles/abstracts/_functions.scss"; -@import "../../../Styles/abstracts/_media-queries.scss"; -@import "../../../Styles/abstracts/_bit-css-variables.scss"; - -.card { - width: 100%; - display: flex; - min-height: rem2(376px); - align-items: flex-start; - border-radius: rem2(2px); - flex-flow: column nowrap; - margin-bottom: rem2(24px); - justify-content: flex-start; - padding: rem2(24px) rem2(16px) rem2(58px); - background-color: $bit-color-background-primary; - border: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding: rem2(24px) rem2(16px) rem2(36px); - } - - @include sm { - width: 100%; - margin-bottom: rem2(16px); - padding: rem2(24px) rem2(8px) rem2(32px); - } -} - -.card-header { - width: 100%; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - padding-bottom: rem2(16px); - justify-content: flex-start; - border-bottom: rem2(1px) solid $bit-color-border-secondary; - - @include md { - padding-bottom: rem2(12px); - } -} - -.title { - font-weight: 600; - font-size: rem2(18px); - line-height: rem2(24px); - margin-bottom: rem2(4px); - - @include md { - font-size: rem2(16px); - line-height: rem2(22px); - } - - @include sm { - font-size: rem2(14px); - line-height: rem2(20px); - } -} - -.subtitle { - font-weight: 400; - font-size: rem2(14px); - line-height: rem2(20px); - - @include md { - font-size: rem2(12px); - line-height: rem2(16px); - } - - @include sm { - font-size: rem2(10px); - line-height: rem2(12px); - } -} - -.card-body { - width: 100%; - display: flex; - align-items: flex-start; - justify-content: flex-start; - padding: rem2(32px) rem2(16px) 0; - - @include md { - padding: rem2(32px) rem2(8px) 0; - } - - @include sm { - padding: rem2(32px) rem2(8px) 0; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor index 11e6e9ac8a..c05631ff55 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor @@ -1,20 +1,80 @@ @attribute [Route(Urls.HomePage)] -@attribute [Route("{culture?}" + Urls.HomePage)] -@inherits AppComponentBase +@attribute [Route("{culture:nonfile?}" + Urls.HomePage)] +@inherits AppPageBase -<PageTitle>@Localizer[nameof(AppStrings.HomeTitle)]</PageTitle> +<PageTitle>@Localizer[nameof(AppStrings.HomePageTitle)]</PageTitle> -<div class="page-container"> - <section class="page-section hero-section"> - <h1 class="hero-section-title">@Localizer[nameof(AppStrings.HomeTitle)]</h1> +<section> + <BitStack Alignment="BitAlignment.Center" Gap="2rem" Class="stack"> + <BitText Typography="BitTypography.H4" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.HomePanelTitle)] + <br /> + @Localizer[nameof(AppStrings.HomePanelSubtitle)] + </BitText> - <div class="hero-section-desc">@Localizer[nameof(AppStrings.HomeMessage)]</div> + <BitText Typography="BitTypography.H6" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.HomeMessage)] + </BitText> - <BitButton Href="https://github.com/bitfoundation/bitplatform/tree/develop/src/Templates/Boilerplate" - Target="_blank" - Variant="BitVariant.Outline" - Title="@Localizer[nameof(AppStrings.GitHubRepo)]"> + <br /> + + <BitButton Target="_blank" + Title="@Localizer[nameof(AppStrings.GitHubRepo)]" + Href="https://github.com/bitfoundation/bitplatform/tree/develop/src/Templates/Boilerplate"> @Localizer[nameof(AppStrings.GitHubRepo)] </BitButton> - </section> -</div> + + <br /> + + <BitCard FullWidth Style="padding:4rem"> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H5">bit platform</BitText> + <BitText Typography="BitTypography.Body1" Align="BitTextAlign.Center">@Localizer[nameof(AppStrings.BitPlatformMessage)]</BitText> + <BitLink Href="https://bitplatform.dev" NoUnderline> + <BitStack Horizontal VerticalAlign="BitAlignment.Baseline" Gap="0.5rem"> + @Localizer[nameof(AppStrings.LearnMore)] + <BitIcon Size="BitSize.Small" IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBack : BitIconName.ChromeBackMirrored)" /> + </BitStack> + </BitLink> + </BitStack> + </BitCard> + + <BitStack Horizontal Gap="2rem" Class="products-stack"> + <BitCard Style="padding:3rem" FullWidth> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H5">bit BlazorUI</BitText> + <BitText Typography="BitTypography.Body1" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.BitBlazorUIMessage)] + </BitText> + <BitButton Variant="BitVariant.Outline" Href="https://www.youtube.com/watch?v=ahgpbDAshmY"> + @Localizer[nameof(AppStrings.WatchVideo)] + </BitButton> + <BitLink Href="https://blazorui.bitplatform.dev/" NoUnderline> + <BitStack Horizontal VerticalAlign="BitAlignment.Baseline" Gap="0.5rem"> + @Localizer[nameof(AppStrings.LearnMore)] + <BitIcon Size="BitSize.Small" IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBack : BitIconName.ChromeBackMirrored)" /> + </BitStack> + </BitLink> + </BitStack> + </BitCard> + + <BitCard Style="padding:3rem" FullWidth> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H5">bit project template</BitText> + <BitText Typography="BitTypography.Body1" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.BitProjectTemplateMessage)] + </BitText> + <BitButton Variant="BitVariant.Outline" Href="https://www.youtube.com/watch?v=UjdWBvb0xac"> + @Localizer[nameof(AppStrings.WatchVideo)] + </BitButton> + <BitLink Href="https://bitplatform.dev/templates/overview/" NoUnderline> + <BitStack Horizontal VerticalAlign="BitAlignment.Baseline" Gap="0.5rem"> + @Localizer[nameof(AppStrings.LearnMore)] + <BitIcon Size="BitSize.Small" IconName="@(currentDir is BitDir.Rtl ? BitIconName.ChromeBack : BitIconName.ChromeBackMirrored)" /> + </BitStack> + </BitLink> + </BitStack> + </BitCard> + </BitStack> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs index b432f85f21..6ce99ea79c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs @@ -2,4 +2,9 @@ public partial class HomePage { + protected override string? Title => Localizer[nameof(AppStrings.Home)]; + protected override string? Subtitle => string.Empty; + + + [CascadingParameter] private BitDir? currentDir { get; set; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss index 425edb78f0..00c2395444 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss @@ -1,87 +1,19 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; +@import '../../Styles/abstracts/_media-queries.scss'; -.page-container { +section { width: 100%; display: flex; - align-items: center; - padding: 0 rem2(16px); - flex-flow: column nowrap; - justify-content: flex-start; + justify-content: center; } -.page-section { - width: 100%; -} - -.hero-section { - width: 100%; - display: flex; - align-items: center; - padding: rem2(144px) 0; - flex-flow: column nowrap; - justify-content: flex-start; - - @include lt-xl { - padding: rem2(130px) 0; - } - - @include sm { - padding: rem2(120px) 0; - } -} - -.hero-section-title { - font-weight: bold; - text-align: center; - font-size: rem2(42px); - line-height: rem2(62px); - margin-bottom: rem2(32px); - - @include lg { - font-size: rem2(34px); - line-height: rem2(56px); - margin-bottom: rem2(24px); - } - - @include md { - font-size: rem2(26px); - line-height: rem2(44px); - margin-bottom: rem2(24px); - } - - @include sm { - font-size: rem2(24px); - line-height: rem2(40px); - margin-bottom: rem2(16px); - } -} - -.hero-section-desc { - text-align: center; - font-size: rem2(24px); - max-width: rem2(1232px); - line-height: rem2(40px); - margin-bottom: rem2(40px); - - @include lg { - font-size: rem2(20px); - max-width: rem2(821px); - line-height: rem2(32px); - margin-bottom: rem2(38px); - } - - @include md { - font-size: rem2(18px); - max-width: rem2(572px); - line-height: rem2(28px); - margin-bottom: rem2(32px); +::deep { + .stack { + max-width: 60rem; } - @include sm { - max-width: 100%; - font-size: rem2(14px); - line-height: rem2(24px); - margin-bottom: rem2(32px); + .products-stack { + @include lt-sm { + flex-wrap: wrap; + } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GitHubIcon.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GitHubIcon.razor index 812783fbff..ccd297c493 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GitHubIcon.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GitHubIcon.razor @@ -1,5 +1,5 @@ @inherits StaticComponent -<svg width="28" height="28" viewBox="0 0 40 40"> +<svg width="34" height="34" viewBox="0 0 40 40"> <path fill="var(--bit-clr-fg-pri)" d="M19.9404 0C8.91388 0 0 8.97959 0 20.0886C0 28.9686 5.71143 36.4853 13.6347 39.1457C14.6253 39.3457 14.9882 38.7135 14.9882 38.1816C14.9882 37.7159 14.9555 36.1196 14.9555 34.4563C9.40857 35.6539 8.25347 32.0616 8.25347 32.0616C7.36204 29.7335 6.04122 29.1351 6.04122 29.1351C4.22571 27.9045 6.17347 27.9045 6.17347 27.9045C8.18735 28.0375 9.24408 29.9665 9.24408 29.9665C11.0265 33.0261 13.8988 32.1616 15.0543 31.6294C15.2192 30.3322 15.7478 29.4343 16.309 28.9355C11.8849 28.4698 7.2302 26.7404 7.2302 19.0241C7.2302 16.829 8.02204 15.0331 9.27673 13.6363C9.07878 13.1376 8.38531 11.0751 9.4751 8.31469C9.4751 8.31469 11.1588 7.78245 14.9551 10.3767C16.5804 9.93701 18.2566 9.71331 19.9404 9.71143C21.6241 9.71143 23.3404 9.94449 24.9253 10.3767C28.722 7.78245 30.4057 8.31469 30.4057 8.31469C31.4955 11.0751 30.8016 13.1376 30.6037 13.6363C31.8914 15.0331 32.6506 16.829 32.6506 19.0241C32.6506 26.7404 27.9959 28.4363 23.5388 28.9355C24.2653 29.5673 24.8922 30.7645 24.8922 32.6604C24.8922 35.3543 24.8596 37.5163 24.8596 38.1812C24.8596 38.7135 25.2229 39.3457 26.2131 39.1461C34.1363 36.4849 39.8478 28.9686 39.8478 20.0886C39.8804 8.97959 30.9339 0 19.9404 0Z" /> </svg> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GoogleIcon.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GoogleIcon.razor index 87f9f58794..8e98ecd74c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GoogleIcon.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/GoogleIcon.razor @@ -1,6 +1,6 @@ @inherits StaticComponent -<svg width="28" height="28" viewBox="0 0 34 34"> +<svg width="34" height="34" viewBox="0 0 34 34"> <path fill="var(--bit-clr-fg-pri)" d="M33.2337 15.4497C33.1503 14.5997 32.4337 13.9663 31.5837 13.9663H19.0003C18.0837 13.9663 17.3337 14.7163 17.3337 15.633V18.483C17.3337 19.3997 18.0837 20.1497 19.0003 20.1497H26.517C26.3337 21.683 25.3337 23.9997 23.117 25.5497C21.7003 26.533 19.817 27.2164 17.3337 27.2164C17.217 27.2164 17.117 27.2163 17.0003 27.1997C12.7503 27.0663 9.15033 24.2164 7.85033 20.2997C7.50033 19.2497 7.30033 18.1497 7.30033 16.9997C7.30033 15.8497 7.50033 14.733 7.83366 13.6997C7.93366 13.3997 8.05033 13.0997 8.18366 12.7997C9.717 9.34969 13.067 6.91635 17.0003 6.79968C17.1003 6.78302 17.217 6.783 17.3337 6.783C19.717 6.783 21.5003 7.56632 22.7503 8.43298C23.4003 8.88298 24.267 8.78299 24.8337 8.23299L27.1503 5.96635C27.8837 5.24968 27.817 4.03298 26.9837 3.43298C24.3337 1.48298 21.1003 0.333008 17.3337 0.333008C17.217 0.333008 17.117 0.333024 17.0003 0.349691C10.617 0.466357 5.13366 4.16636 2.45033 9.51636C1.31699 11.783 0.666992 14.3163 0.666992 16.9997C0.666992 19.683 1.31699 22.2163 2.45033 24.483H2.467C5.15033 29.833 10.6337 33.533 17.0003 33.6497C17.117 33.6663 17.217 33.6663 17.3337 33.6663C21.8337 33.6663 25.617 32.183 28.367 29.633C31.517 26.7163 33.3337 22.4497 33.3337 17.3663C33.3337 16.6497 33.3003 16.033 33.2337 15.4497Z" /> </svg> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor new file mode 100644 index 0000000000..8eb8079a6e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor @@ -0,0 +1,35 @@ +@inherits AppComponentBase + +<section> + <BitStack Horizontal Alignment="BitAlignment.Center" Gap="3rem"> + <BitButton Size="BitSize.Small" + Variant="BitVariant.Text" + Style="padding-block:0.5rem" + ButtonType="BitButtonType.Button" + OnClick="WrapHandled(HandleGoogle)" + Color="BitColor.SecondaryBackground" + Title="@Localizer[AppStrings.GoogleSignUpButtonText]"> + <GoogleIcon /> + </BitButton> + + <BitButton Size="BitSize.Small" + Variant="BitVariant.Text" + Style="padding-block:0.5rem" + ButtonType="BitButtonType.Button" + OnClick="WrapHandled(HandleGitHub)" + Color="BitColor.SecondaryBackground" + Title="@Localizer[AppStrings.GitHubSignUpButtonText]"> + <GitHubIcon /> + </BitButton> + + <BitButton Size="BitSize.Small" + Variant="BitVariant.Text" + Style="padding-block:0.5rem" + ButtonType="BitButtonType.Button" + OnClick="WrapHandled(HandleTwitter)" + Color="BitColor.SecondaryBackground" + Title="@Localizer[AppStrings.TwitterSignUpButtonText]"> + <TwitterIcon /> + </BitButton> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.cs new file mode 100644 index 0000000000..89a90a41d1 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.cs @@ -0,0 +1,22 @@ +namespace Boilerplate.Client.Core.Components.Pages.Identity.Components; + +public partial class SocialRow +{ + [Parameter] public EventCallback<string> OnClick { get; set; } + + + private async Task HandleGoogle() + { + await OnClick.InvokeAsync("Google"); + } + + private async Task HandleGitHub() + { + await OnClick.InvokeAsync("GitHub"); + } + + private async Task HandleTwitter() + { + await OnClick.InvokeAsync("Twitter"); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.scss new file mode 100644 index 0000000000..6c769886a9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/SocialRow.razor.scss @@ -0,0 +1,3 @@ +section { + margin-top: 1rem; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/TwitterIcon.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/TwitterIcon.razor index cf7c50cb06..07da599364 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/TwitterIcon.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Components/TwitterIcon.razor @@ -1,6 +1,6 @@ @inherits StaticComponent -<svg width="28" height="28" viewBox="0 0 31 32"> +<svg width="34" height="34" viewBox="0 0 31 32"> <path fill="var(--bit-clr-fg-pri)" d="M1.53555 0.799805L12.909 17.3811L0.982422 31.1998H3.52461L14.0355 19.0217L22.3887 31.1998H30.3996L18.498 13.8498L29.7621 0.799805H27.2215L17.373 12.2092L9.54648 0.799805H1.53555Z" /> </svg> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor index 7b08318bb8..944fa04ae3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor @@ -1,157 +1,154 @@ @*+:cnd:noEmit*@ @attribute [Route(Urls.ConfirmPage)] @attribute [Route("{culture?}" + Urls.ConfirmPage)] -@inherits AppComponentBase +@inherits AppPageBase -<PageTitle>@Localizer[nameof(AppStrings.ConfirmTitle)]</PageTitle> +<PageTitle>@Localizer[nameof(AppStrings.ConfirmPageTitle)]</PageTitle> -<div class="page-container"> - <div class="form"> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(errorMessage) is false) - { - <BitMessage Multiline Color="BitColor.Error" OnDismiss="() => errorMessage = null"> - @errorMessage - </BitMessage> - } - </div> +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H4" Gutter>@Localizer[nameof(AppStrings.ConfirmTitle)]</BitText> - <BitText Typography="BitTypography.H4" Gutter> - @Localizer[nameof(AppStrings.ConfirmTitle)] - </BitText> - <br /> - <BitPivot> + <BitPivot Alignment="BitAlignment.Center" Class="confirm-pivot"> @if (showEmailConfirmation) { - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.ConfirmEmailHeaderText)]" IconName="@(isEmailConfirmed ? BitIconName.CheckMark : null)"> - <br /> - @if (isEmailConfirmed is false) - { - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.ConfirmEmailSubtitle)] - <br /> - @Localizer[nameof(AppStrings.ConfirmEmailMessage)] - </BitText> - <br /> - <EditForm Model="emailModel" OnValidSubmit="WrapHandled(ConfirmEmail)" class="confirm-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="emailModel.Email" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.Email)]" - IsEnabled="string.IsNullOrEmpty(EmailQueryString)" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => emailModel.Email)" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="emailModel.Token" - Type="BitInputType.Number" - AutoComplete="@BitAutoCompleteValue.OneTimeCode" - Label="@Localizer[nameof(AppStrings.EmailToken)]" - Placeholder="@Localizer[nameof(AppStrings.EmailTokenPlaceholder)]" /> - <ValidationMessage For="@(() => emailModel.Token)" /> - </div> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.ConfirmEmailHeaderText)]" + IconName="@(isEmailConfirmed ? BitIconName.CheckMark : null)"> + <BitStack FillContent> + @if (isEmailConfirmed is false) + { + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.ConfirmEmailSubtitle)] + @Localizer[nameof(AppStrings.ConfirmEmailMessage)] + </BitText> + + <EditForm Model="emailModel" OnValidSubmit="WrapHandled(ConfirmEmail)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="emailModel.Email" + AutoFocus TabIndex="1" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.Email)]" + IsEnabled="string.IsNullOrEmpty(EmailQueryString)" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => emailModel.Email)" /> + + <BitTextField @bind-Value="emailModel.Token" + TabIndex="2" + Type="BitInputType.Number" + AutoComplete="@BitAutoCompleteValue.OneTimeCode" + Label="@Localizer[nameof(AppStrings.EmailToken)]" + Placeholder="@Localizer[nameof(AppStrings.EmailTokenPlaceholder)]" /> + <ValidationMessage For="@(() => emailModel.Token)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.EmailTokenConfirmButtonText)] + </BitButton> + </BitStack> + </EditForm> <br /> - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.EmailTokenConfirmButtonText)] + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.NotReceivedEmailMessage)] + </BitText> + + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.CheckSpamMailMessage)] + </BitText> + + <BitButton IsLoading="isWaiting" + ButtonType="BitButtonType.Button" + Variant="BitVariant.Outline" + OnClick="WrapHandled(ResendEmailToken)"> + @Localizer[nameof(AppStrings.ResendEmailTokenButtonText)] </BitButton> - </EditForm> - <br /> - <br /> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.NotReceivedConfirmationEmailMessage)] - </BitText> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.CheckSpamMailMessage)] - </BitText> - <br /> - <BitButton IsLoading="isWaiting" - ButtonType="BitButtonType.Button" - Variant="BitVariant.Outline" - OnClick="WrapHandled(ResendEmailToken)"> - @Localizer[nameof(AppStrings.ResendEmailTokenButtonText)] - </BitButton> - } - else - { - <BitText Typography="BitTypography.H6" Gutter> - @Localizer[nameof(AppStrings.EmailConfirmationSuccessTitle), emailModel.Email!] - </BitText> - <br /> - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.EmailConfirmationSuccessMessage)] - </BitText> - <br /> - <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> - } + } + else + { + <BitText Typography="BitTypography.H6" Gutter> + @Localizer[nameof(AppStrings.EmailConfirmationSuccessTitle), emailModel.Email!] + </BitText> + + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.EmailConfirmationSuccessMessage)] + </BitText> + + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + } + </BitStack> </BitPivotItem> } @if (showPhoneConfirmation) { - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.ConfirmPhoneHeaderText)]" IconName="@(isPhoneConfirmed ? BitIconName.CheckMark : null)"> - <br /> - @if (isPhoneConfirmed is false) - { - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.ConfirmPhoneSubtitle)] - <br /> - @Localizer[nameof(AppStrings.ConfirmPhoneMessage)] - </BitText> - <br /> - <EditForm Model="phoneModel" OnValidSubmit="WrapHandled(ConfirmPhone)" class="confirm-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="phoneModel.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - IsEnabled="string.IsNullOrEmpty(PhoneNumberQueryString)" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => phoneModel.PhoneNumber)" /> - </div> - <div class="form-input-container"> - <BitTextField @bind-Value="phoneModel.Token" - Type="BitInputType.Number" - AutoComplete="@BitAutoCompleteValue.OneTimeCode" - Label="@Localizer[nameof(AppStrings.PhoneToken)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneTokenPlaceholder)]" /> - <ValidationMessage For="@(() => phoneModel.Token)" /> - </div> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.ConfirmPhoneHeaderText)]" + IconName="@(isPhoneConfirmed ? BitIconName.CheckMark : null)"> + <BitStack FillContent> + @if (isPhoneConfirmed is false) + { + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.ConfirmPhoneSubtitle)] + @Localizer[nameof(AppStrings.ConfirmPhoneMessage)] + </BitText> + + <EditForm Model="phoneModel" OnValidSubmit="WrapHandled(ConfirmPhone)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent> + <BitTextField @bind-Value="phoneModel.PhoneNumber" + AutoFocus TabIndex="1" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + IsEnabled="string.IsNullOrEmpty(PhoneNumberQueryString)" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => phoneModel.PhoneNumber)" /> + + <BitTextField @bind-Value="phoneModel.Token" + TabIndex="2" + Type="BitInputType.Number" + AutoComplete="@BitAutoCompleteValue.OneTimeCode" + Label="@Localizer[nameof(AppStrings.PhoneToken)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneTokenPlaceholder)]" /> + <ValidationMessage For="@(() => phoneModel.Token)" /> + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.PhoneTokenConfirmButtonText)] + </BitButton> + </BitStack> + </EditForm> <br /> - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.PhoneTokenConfirmButtonText)] + <BitText Typography="BitTypography.Body1" Gutter> + @Localizer[nameof(AppStrings.NotReceivedPhoneMessage)] + </BitText> + + <BitButton IsLoading="isWaiting" + ButtonType="BitButtonType.Button" + Variant="BitVariant.Outline" + OnClick="WrapHandled(ResendPhoneToken)"> + @Localizer[nameof(AppStrings.ResendPhoneTokenButtonText)] </BitButton> - </EditForm> - <br /> - <br /> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.NotReceivedConfirmationPhoneMessage)] - </BitText> - <br /> - <BitButton IsLoading="isWaiting" - ButtonType="BitButtonType.Button" - Variant="BitVariant.Outline" - OnClick="WrapHandled(ResendPhoneToken)"> - @Localizer[nameof(AppStrings.ResendPhoneTokenButtonText)] - </BitButton> - } - else - { - <BitText Typography="BitTypography.H5" Gutter> - @Localizer[nameof(AppStrings.PhoneConfirmationSuccessTitle), phoneModel.PhoneNumber!] - </BitText> - <br /> - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.PhoneConfirmationSuccessMessage)] - </BitText> - <br /> - <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> - } + } + else + { + <BitText Typography="BitTypography.H5" Gutter> + @Localizer[nameof(AppStrings.PhoneConfirmationSuccessTitle), phoneModel.PhoneNumber!] + </BitText> + + <BitText Typography="BitTypography.Subtitle1" Gutter> + @Localizer[nameof(AppStrings.PhoneConfirmationSuccessMessage)] + </BitText> + + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + } + </BitStack> </BitPivotItem> } </BitPivot> - </div> -</div> + <br /> + <BitStack Horizontal HorizontalAlign="BitAlignment.Center"> + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + <BitText>@Localizer[nameof(AppStrings.Or)]</BitText> + <BitLink Href="@Urls.SignUpPage">@Localizer[nameof(AppStrings.SignUp)]</BitLink> + </BitStack> + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.cs index 5f59a74106..ef0fd495cf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.cs @@ -13,10 +13,6 @@ public partial class ConfirmPage private readonly ConfirmEmailRequestDto emailModel = new(); private readonly ConfirmPhoneRequestDto phoneModel = new(); - private string? errorMessage; - private ElementReference messageRef = default!; - - [AutoInject] private IIdentityController identityController = default!; @@ -128,7 +124,6 @@ await WrapRequest(async () => private async Task WrapRequest(Func<Task> action) { isWaiting = true; - errorMessage = null; try { @@ -136,8 +131,7 @@ private async Task WrapRequest(Func<Task> action) } catch (KnownException e) { - errorMessage = e.Message; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.scss index a181381fe6..189bd821d2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ConfirmPage.razor.scss @@ -1,17 +1,12 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; +@import '../../../Styles/abstracts/_media-queries.scss'; -.page-container { +section { width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; } -::deep .confirm-form { - display: flex; - flex-flow: column; - align-items: center; +::deep { + .confirm-pivot { + width: 304px; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor index a51aeed002..502e669d22 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor @@ -1,58 +1,61 @@ @*+:cnd:noEmit*@ @attribute [Route(Urls.ForgotPasswordPage)] @attribute [Route("{culture?}" + Urls.ForgotPasswordPage)] -@inherits AppComponentBase +@inherits AppPageBase -<PageTitle>@Localizer[nameof(AppStrings.ForgotPassword)]</PageTitle> +<PageTitle>@Localizer[nameof(AppStrings.ForgotPasswordPageTitle)]</PageTitle> -<div class="page-container"> - <div class="form"> - @if (string.IsNullOrEmpty(errorMessage) is false) - { - <BitMessage Class="form-message-bar" Color="BitColor.Error" OnDismiss="() => errorMessage = null"> - @errorMessage - </BitMessage> - } +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H4">@Localizer[nameof(AppStrings.ForgotPasswordTitle)]</BitText> - <BitText Typography="BitTypography.H4" Gutter> - @Localizer[nameof(AppStrings.ForgotPasswordTitle)] - </BitText> - - <BitText Typography="BitTypography.Subtitle1" Gutter> + <BitText Typography="BitTypography.Subtitle1" Align="BitTextAlign.Center"> @Localizer[nameof(AppStrings.ForgotPasswordMessage)] </BitText> - <br /> - - <EditForm Model="model" OnValidSubmit="WrapHandled(Submit)" class="forgot-form"> + <EditForm Model="model" OnValidSubmit="WrapHandled(Submit)" novalidate> <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="model.Email" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.Email)]" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => model.Email)" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="model.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => model.PhoneNumber)" /> - </div> - - <BitButton IsLoading="isWaiting" - Class="form-submit-button" - ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.Submit)] - </BitButton> + <BitStack FillContent Gap="2rem"> + <BitStack FillContent> + <BitPivot Alignment="BitAlignment.Center" DefaultSelectedKey="@EmailKey" OnChange="OnPivotChange"> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]" Key="@EmailKey"> + <BitTextField @bind-Value="model.Email" + AutoFocus + Type="BitInputType.Email" + Immediate DebounceTime="500" + Label="@Localizer[nameof(AppStrings.Email)]" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => model.Email)" /> + </BitPivotItem> + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.PhoneNumber)]" Key="@PhoneKey"> + <BitTextField @bind-Value="model.PhoneNumber" + AutoFocus + Type="BitInputType.Tel" + Immediate DebounceTime="500" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => model.PhoneNumber)" /> + </BitPivotItem> + </BitPivot> + </BitStack> + <BitButton IsLoading="isWaiting" + Class="form-submit-button" + ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Submit)] + </BitButton> + </BitStack> </EditForm> <br /> <div> @Localizer[nameof(AppStrings.ResetPasswordMessageInForgot)] <BitLink Href="@Urls.ResetPasswordPage">@Localizer[nameof(AppStrings.ResetPassword)]</BitLink> </div> - </div> -</div> + + <BitStack Horizontal HorizontalAlign="BitAlignment.Center"> + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + <BitText>@Localizer[nameof(AppStrings.Or)]</BitText> + <BitLink Href="@Urls.SignUpPage">@Localizer[nameof(AppStrings.SignUp)]</BitLink> + </BitStack> + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.cs index 8e03b7236e..a743144482 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.cs @@ -9,15 +9,30 @@ public partial class ForgotPasswordPage [AutoInject] IIdentityController identityController = default!; private bool isWaiting; - private string? errorMessage; private readonly SendResetPasswordTokenRequestDto model = new(); + private const string EmailKey = nameof(EmailKey); + private const string PhoneKey = nameof(PhoneKey); + + + private void OnPivotChange(BitPivotItem item) + { + if (item.Key == EmailKey) + { + model.PhoneNumber = null; + } + + if (item.Key == PhoneKey) + { + model.Email = null; + } + } + private async Task Submit() { if (isWaiting) return; isWaiting = true; - errorMessage = null; try { @@ -37,7 +52,7 @@ private async Task Submit() } catch (KnownException e) { - errorMessage = e.Message; + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.scss index 54b6de8e9a..0fb1d9708a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ForgotPasswordPage.razor.scss @@ -1,18 +1,12 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; +@import '../../../Styles/abstracts/_media-queries.scss'; -.page-container { +section { width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; } -::deep .forgot-form { - width: 100%; - display: flex; - flex-flow: column; - align-items: center; +::deep { + form { + width: 304px; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor deleted file mode 100644 index 694a57c12b..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor +++ /dev/null @@ -1,116 +0,0 @@ -@inherits AppComponentBase - -<div style="width:100%"> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Color="messageColor" OnDismiss="() => message = null">@message</BitMessage> - } - </div> - - <BitText Typography="BitTypography.H2"> - @Localizer[nameof(AppStrings.ChangeEmailTitle)] - </BitText> - - <br /> - - @if (Loading) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - if (showConfirmation is false) - { - <EditForm Model="sendModel" OnValidSubmit="WrapHandled(SendToken)" class="change-email-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitLabel>@Localizer[nameof(AppStrings.CurrentEmail)]</BitLabel> - <BitTag Variant="BitVariant.Outline" Text="@Email" Style="width:100%;max-width:unset" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="sendModel.Email" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.NewEmail)]" - Placeholder="@Localizer[nameof(AppStrings.NewEmailPlaceholder)]" /> - <ValidationMessage For="@(() => sendModel.Email)" /> - </div> - - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.Submit)] - </BitButton> - <br /> - <div> - @Localizer[nameof(AppStrings.ConfirmMessageInProfile)] - <BitButton ButtonType="BitButtonType.Button" - Variant="BitVariant.Text" - OnClick="() => showConfirmation = true"> - @Localizer[nameof(AppStrings.Confirm)] - </BitButton> - </div> - </EditForm> - } - else - { - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.ConfirmEmailSubtitle)] - <br /> - @Localizer[nameof(AppStrings.ConfirmEmailMessage)] - </BitText> - <br /> - <EditForm Model="changeModel" OnValidSubmit="WrapHandled(ChangeEmail)" class="change-email-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="changeModel.Email" - IsEnabled="isEmailUnavailable" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.Email)]" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => changeModel.Email)" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="changeModel.Token" - Type="BitInputType.Number" - Label="@Localizer[nameof(AppStrings.EmailToken)]" - Placeholder="@Localizer[nameof(AppStrings.EmailTokenPlaceholder)]" /> - <ValidationMessage For="@(() => changeModel.Token)" /> - </div> - <br /> - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.EmailTokenConfirmButtonText)] - </BitButton> - <br /> - <BitButton ButtonType="BitButtonType.Button" - Variant="BitVariant.Text" - IconName="@BitIconName.Back" - OnClick="GoBack"> - @Localizer[nameof(AppStrings.GoBack)] - </BitButton> - </EditForm> - @if (isEmailUnavailable is false) - { - <br /> - <br /> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.NotReceivedConfirmationEmailMessage)] - </BitText> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.CheckSpamMailMessage)] - </BitText> - <br /> - <BitButton IsLoading="isWaiting" - ButtonType="BitButtonType.Button" - Variant="BitVariant.Outline" - OnClick="WrapHandled(SendToken)"> - @Localizer[nameof(AppStrings.ResendEmailTokenButtonText)] - </BitButton> - } - } - } -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.scss deleted file mode 100644 index f47a5ca349..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangeEmailSection.razor.scss +++ /dev/null @@ -1,7 +0,0 @@ -::deep .change-email-form { - width: 100%; - display: flex; - align-items: center; - flex-flow: column nowrap; - justify-content: flex-start; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor deleted file mode 100644 index 12e732a02c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor +++ /dev/null @@ -1,113 +0,0 @@ -@inherits AppComponentBase - -<div style="width:100%"> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Color="messageColor" OnDismiss="() => message = null">@message</BitMessage> - } - </div> - - <BitText Typography="BitTypography.H2"> - @Localizer[nameof(AppStrings.ChangePhoneNumberTitle)] - </BitText> - - <br /> - - @if (Loading) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - if (showConfirmation is false) - { - <EditForm Model="sendModel" OnValidSubmit="WrapHandled(SendToken)" class="change-phone-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitLabel>@Localizer[nameof(AppStrings.CurrentPhoneNumber)]</BitLabel> - <BitTag Variant="BitVariant.Outline" Text="@PhoneNumber" Style="width:100%;max-width:unset" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="sendModel.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.NewPhoneNumber)]" - Placeholder="@Localizer[nameof(AppStrings.NewPhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => sendModel.PhoneNumber)" /> - </div> - - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.Submit)] - </BitButton> - <br /> - <div> - @Localizer[nameof(AppStrings.ConfirmMessageInProfile)] - <BitButton ButtonType="BitButtonType.Button" - Variant="BitVariant.Text" - OnClick="() => showConfirmation = true"> - @Localizer[nameof(AppStrings.Confirm)] - </BitButton> - </div> - </EditForm> - } - else - { - <BitText Typography="BitTypography.Subtitle1" Gutter> - @Localizer[nameof(AppStrings.ConfirmPhoneSubtitle)] - <br /> - @Localizer[nameof(AppStrings.ConfirmPhoneMessage)] - </BitText> - <br /> - <EditForm Model="changeModel" OnValidSubmit="WrapHandled(ChangePhoneNumber)" class="change-phone-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="changeModel.PhoneNumber" - IsEnabled="isPhoneNumberUnavailable" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => changeModel.PhoneNumber)" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="changeModel.Token" - Type="BitInputType.Number" - Label="@Localizer[nameof(AppStrings.PhoneToken)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneTokenPlaceholder)]" /> - <ValidationMessage For="@(() => changeModel.Token)" /> - </div> - <br /> - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.PhoneTokenConfirmButtonText)] - </BitButton> - <br /> - <BitButton ButtonType="BitButtonType.Button" - Variant="BitVariant.Text" - IconName="@BitIconName.Back" - OnClick="GoBack"> - @Localizer[nameof(AppStrings.GoBack)] - </BitButton> - </EditForm> - @if (isPhoneNumberUnavailable is false) - { - <br /> - <br /> - <BitText Typography="BitTypography.Body1" Gutter> - @Localizer[nameof(AppStrings.NotReceivedConfirmationPhoneMessage)] - </BitText> - <br /> - <BitButton IsLoading="isWaiting" - ButtonType="BitButtonType.Button" - Variant="BitVariant.Outline" - OnClick="WrapHandled(SendToken)"> - @Localizer[nameof(AppStrings.ResendPhoneTokenButtonText)] - </BitButton> - } - } - } -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.scss deleted file mode 100644 index a80b50a0e5..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.scss +++ /dev/null @@ -1,7 +0,0 @@ -::deep .change-phone-form { - width: 100%; - display: flex; - align-items: center; - flex-flow: column nowrap; - justify-content: flex-start; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor deleted file mode 100644 index 736e2d7911..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor +++ /dev/null @@ -1,23 +0,0 @@ -@inherits AppComponentBase - -<BitModal @bind-IsOpen="IsOpen" IsBlocking="true" AutoToggleScroll="false"> - <div class="modal-body"> - <div class="modal-close-btn-container"> - <BitButton OnClick="CloseModal" IconName="@BitIconName.ChromeClose" Title="Close" Variant="BitVariant.Text" /> - </div> - <div class="modal-title"> - @Localizer[nameof(AppStrings.DeleteAccount)] - </div> - <div class="modal-desc"> - @Localizer[nameof(AppStrings.DeleteAccountPrompt)] - </div> - <div> - <BitButton OnClick="DeleteAccount"> - @Localizer[nameof(AppStrings.Yes)] - </BitButton> - <BitButton OnClick="CloseModal" Variant="BitVariant.Outline"> - @Localizer[nameof(AppStrings.No)] - </BitButton> - </div> - </div> -</BitModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.cs deleted file mode 100644 index d15826e648..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Boilerplate.Shared.Controllers.Identity; - -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; - -public partial class DeleteAccountConfirmModal -{ - [AutoInject] IUserController userController = default!; - - [Parameter] public bool IsOpen { get; set; } - - [Parameter] public EventCallback<bool> IsOpenChanged { get; set; } - - private async Task CloseModal() - { - IsOpen = false; - - await IsOpenChanged.InvokeAsync(false); - } - - private async Task DeleteAccount() - { - await userController.Delete(CurrentCancellationToken); - - await AuthenticationManager.SignOut(CurrentCancellationToken); - - await CloseModal(); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.scss deleted file mode 100644 index f04991a025..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import '../../../../Styles/abstracts/_functions.scss'; -@import '../../../../Styles/abstracts/_media-queries.scss'; -@import '../../../../Styles/abstracts/_bit-css-variables.scss'; - - -.modal-body { - display: flex; - position: relative; - align-items: center; - justify-content: center; - flex-flow: column nowrap; - padding: rem2(28px) rem2(110px); - - @include lt-lg { - padding: rem2(28px) rem2(47px); - } -} - -.modal-close-btn-container { - position: absolute; - inset-inline-end: 0; - inset-block-start: 0; -} - -.modal-title { - font-weight: 600; - font-size: rem2(24px); - color: $bit-color-error; - line-height: rem2(40px); - margin-bottom: rem2(12px); -} - -.modal-desc { - font-size: rem2(18px); - line-height: rem2(24px); - margin-bottom: rem2(36px); -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor deleted file mode 100644 index 16ec53ce4c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor +++ /dev/null @@ -1,19 +0,0 @@ -@inherits AppComponentBase - -<BitText Typography="BitTypography.H5" Class="@BitCss.Class.Color.Error.Main"> - @Localizer[nameof(AppStrings.DeleteAccount)] -</BitText> -<br /> -<br /> -<BitText Typography="BitTypography.Body1"> - @Localizer[nameof(AppStrings.DeleteAccountPrompt)] -</BitText> -<br /> -<br /> -<BitButton Color="BitColor.Error" - Variant="BitVariant.Outline" - OnClick="() => isDeleteAccountConfirmModalOpen = true"> - @Localizer[nameof(AppStrings.DeleteAccount)] -</BitButton> - -<DeleteAccountConfirmModal @bind-IsOpen="isDeleteAccountConfirmModalOpen" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.cs deleted file mode 100644 index d6014ccfd5..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; - -public partial class DeleteAccountSection -{ - private bool isDeleteAccountConfirmModalOpen; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.scss deleted file mode 100644 index e47a19e894..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/DeleteAccountSection.razor.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '../../../../Styles/abstracts/_bit-css-variables.scss'; - -.title { - color: $bit-color-error; - border-bottom: 1px solid $bit-color-error; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor deleted file mode 100644 index 2913e3b456..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor +++ /dev/null @@ -1,42 +0,0 @@ -@*+:cnd:noEmit*@ -@attribute [Route(Urls.ProfilePage)] -@attribute [Route("{culture?}" + Urls.ProfilePage)] -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.ProfileTitle)]</PageTitle> - -<div class="page-container"> - <div class="content-container profile-panel"> - <UserDataSection User="user" Loading="isLoading" /> - </div> - - <br /> - - <div class="content-container tfa-panel"> - <TwoFactorSection /> - </div> - - <br /> - - <div class="content-container email-panel"> - <ChangeEmailSection Email="@user?.Email" Loading="isLoading" /> - </div> - - <br /> - - <div class="content-container email-panel"> - <ChangePhoneNumberSection PhoneNumber="@user?.PhoneNumber" Loading="isLoading" /> - </div> - - <br /> - - <div class="content-container sessions-panel"> - <UserSessionsSection /> - </div> - - <br /> - - <div class="content-container danger-panel"> - <DeleteAccountSection /> - </div> -</div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.cs deleted file mode 100644 index 21aa017abb..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.cs +++ /dev/null @@ -1,31 +0,0 @@ -//+:cnd:noEmit -using Boilerplate.Shared.Dtos.Identity; -using Boilerplate.Shared.Controllers.Identity; - -namespace Boilerplate.Client.Core.Components.Pages.Identity.Profile; - -[Authorize] -public partial class ProfilePage -{ - private UserDto? user; - private bool isLoading; - - [AutoInject] private IUserController userController = default!; - - - protected override async Task OnInitAsync() - { - isLoading = true; - - try - { - user = await userController.GetCurrentUser(CurrentCancellationToken); - } - finally - { - isLoading = false; - } - - await base.OnInitAsync(); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.scss deleted file mode 100644 index 94aef88316..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/ProfilePage.razor.scss +++ /dev/null @@ -1,50 +0,0 @@ -@import '../../../../Styles/abstracts/_functions.scss'; -@import '../../../../Styles/abstracts/_media-queries.scss'; -@import '../../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} - -.content-container { - width: 100%; - display: flex; - position: relative; - padding: rem2(32px); - align-items: center; - max-width: rem2(608px); - border-radius: rem2(4px); - flex-flow: column nowrap; - background-color: $bit-color-background-primary; - - @include lt-md { - padding: 1rem; - } - - &.profile-panel { - min-height: rem2(560px); - box-shadow: $bit-box-shadow-callout; - } - - &.tfa-panel { - box-shadow: $bit-box-shadow-callout; - } - - &.email-panel { - text-align: center; - box-shadow: $bit-box-shadow-callout; - } - - &.sessions-panel { - box-shadow: $bit-box-shadow-callout; - } - - &.danger-panel { - border: 2px solid $bit-color-error; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor deleted file mode 100644 index 53859ed0ee..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor +++ /dev/null @@ -1,167 +0,0 @@ -@inherits AppComponentBase - -<div style="width:100%"> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Multiline Color="BitColor.Error" OnDismiss="() => message = null">@message</BitMessage> - } - </div> - - <BitText Typography="BitTypography.H2" Style="text-align:center" Gutter> - @Localizer[nameof(AppStrings.TwoFactorAuthTitle)] - </BitText> - - @if (isWaiting) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - @if (isTwoFactorAuthEnabled is false) - { - <h3>@Localizer[nameof(AppStrings.TfaConfigureAutAppTitle)]</h3> - <br /> - <div> - <p>@Localizer[nameof(AppStrings.TfaConfigureAutAppSubtitle)]</p> - <ol style="padding-inline-start: 1rem;"> - <li> - <p> - @(new MarkupString(Localizer[nameof(AppStrings.TfaConfigureAutAppStep1), - "<a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en\">Android</a>", - "<a href=\"https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8\">iOS</a>"])) - </p> - </li> - <br /> - <li> - @Localizer[nameof(AppStrings.TfaConfigureAutAppStep2)] - <a class="qr-code-container" href="@authenticatorUri"> - <img src="data:image/png;base64,@qrCode" style="width:256px" /> - </a> - <div class="tfa-key-container"> - <div class="tfa-key">@sharedKey</div> - <BitButton Title="Copy" - Style="padding:0" - Variant="BitVariant.Text" - OnClick="WrapHandled(CopySharedKeyToClipboard)" - IconName="@(isKeyCopiedShown ? "" : BitIconName.Copy)"> - @(isKeyCopiedShown ? "Copied" : "") - </BitButton> - </div> - </li> - <br /> - <li> - @Localizer[nameof(AppStrings.TfaConfigureAutAppStep3)] - <br /> - <br /> - <div> - <BitTextField @bind-Value="verificationCode" - Label="@Localizer[nameof(AppStrings.TfaConfigureAutAppVerificationCodeLabel)]" - AutoComplete="@BitAutoCompleteValue.Off" - Placeholder="@Localizer[nameof(AppStrings.TfaConfigureAutAppVerificationCodePlaceholder)]" /> - <br /> - <BitButton OnClick="WrapHandled(EnableTwoFactorAuth)"> - @Localizer[nameof(AppStrings.TfaConfigureAutAppVerifyButtonText)] - </BitButton> - </div> - </li> - </ol> - </div> - } - else - { - <BitPivot OverflowBehavior="BitPivotOverflowBehavior.Scroll"> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaRecoveryCodesHeader)]"> - <div class="tab-item-container"> - @if (recoveryCodesLeft == 0) - { - <BitMessage Color="BitColor.Error" Multiline Style="margin-bottom:1rem"> - <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesZeroLeftTitle)]</strong> - <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesZeroLeftSubtitle)]</p> - </BitMessage> - } - else if (recoveryCodesLeft == 1) - { - <BitMessage Color="BitColor.SevereWarning" Multiline Style="margin-bottom:1rem"> - <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesOneLeftTitle)]</strong> - <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesOneLeftSubtitle)]</p> - </BitMessage> - } - else if (recoveryCodesLeft <= 3) - { - <BitMessage Color="BitColor.Warning" Multiline Style="margin-bottom:1rem"> - <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesThreeLeftTitle), recoveryCodesLeft]</strong> - <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesThreeLeftSubtitle)]</p> - </BitMessage> - } - - @if (recoveryCodes is null) - { - <BitMessage Color="BitColor.Warning" Multiline Style="margin-bottom:1rem"> - @Localizer[nameof(AppStrings.TfaRecoveryCodesGenerateWraning)] - </BitMessage> - - <BitButton OnClick="WrapHandled(GenerateRecoveryCode)"> - @Localizer[nameof(AppStrings.TfaRecoveryCodesGenerateButtonText)] - </BitButton> - } - else - { - <BitMessage Color="BitColor.Warning" Multiline Style="margin: 1rem 0"> - <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesWarningTitle)]</strong> - <br /> - <p>@Localizer[nameof(AppStrings.TfaRecoveryCodesWarning)]</p> - </BitMessage> - - <BitMessage Color="BitColor.Info" Multiline Class="copy-recovery-con"> - <div style="display:flex"> - <div style="flex-grow:1"> - <strong>@Localizer[nameof(AppStrings.TfaRecoveryCodesTitle)]</strong> - </div> - <BitButton OnClick="WrapHandled(CopyRecoveryCodesToClipboard)" - IconName="@(isCodesCopiedShown ? "" : BitIconName.Copy)" - Title="Copy" Style="padding:0" - Variant="BitVariant.Outline"> - @(isCodesCopiedShown ? "Copied" : "") - </BitButton> - </div> - @foreach (var recoveryCode in recoveryCodes) - { - <div class="recovery-code">@recoveryCode</div> - } - - </BitMessage> - } - </div> - </BitPivotItem> - - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaAuthAppHeader)]"> - <div class="tab-item-container"> - <BitMessage Color="BitColor.Warning" Multiline Style="margin:1rem 0"> - <strong>@Localizer[nameof(AppStrings.TfaAuthAppWarningTitle)]</strong> - <p>@Localizer[nameof(AppStrings.TfaAuthAppWarning)]</p> - </BitMessage> - <BitButton OnClick="WrapHandled(ResetAuthenticatorKey)"> - @Localizer[nameof(AppStrings.TfaAuthAppResetKeyButtonText)] - </BitButton> - </div> - </BitPivotItem> - - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TfaDisable2faHeader)]"> - <div class="tab-item-container"> - <BitMessage Color="BitColor.Warning" Multiline Style="margin:1rem 0"> - <strong>@Localizer[nameof(AppStrings.TfaDisable2faWarningTitle)]</strong> - <p>@Localizer[nameof(AppStrings.TfaDisable2faWarning)]</p> - </BitMessage> - - <BitButton OnClick="WrapHandled(DisableTwoFactorAuth)"> - @Localizer[nameof(AppStrings.TfaDisable2faButtonText)] - </BitButton> - </div> - </BitPivotItem> - </BitPivot> - } - } -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.scss deleted file mode 100644 index a6a1cb9dfd..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/TwoFactorSection.razor.scss +++ /dev/null @@ -1,39 +0,0 @@ -@import '../../../../Styles/abstracts/_bit-css-variables.scss'; - -::deep { - .tfa-message-bar { - inset-block-end: 0; - inset-block-start: unset; - } -} - -.qr-code-container { - display: flex; - margin: 1rem 0; - justify-content: center; -} - -.tfa-key-container { - display: flex; - padding: 1rem; - margin: 1rem 0; - align-items: center; - border: 1px solid $bit-color-border-secondary; - background-color: $bit-color-background-secondary; -} - -.tfa-key { - flex-grow: 1; - font-size: 1rem; - font-weight: bold; - font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; -} - -.tab-item-container { - margin-top: 1rem; -} - -.recovery-code { - margin-top: 0.5rem; - font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor deleted file mode 100644 index 4c1bffe6fc..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor +++ /dev/null @@ -1,100 +0,0 @@ -@inherits AppComponentBase - -<div> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Multiline Color="messageColor" OnDismiss="() => message = null">@message</BitMessage> - } - </div> - - <BitText Typography="BitTypography.H2"> - @Localizer[nameof(AppStrings.EditProfileTitle)] - </BitText> - - <br /> - - @if (Loading) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - var imageUrl = user.ProfileImageName is null ? null : $"{profileImageUrl}&file={user.ProfileImageName}"; - <div class="form-input-container"> - <BitPersona ImageUrl="@imageUrl" - Size=@BitPersonaSize.Size72 - PrimaryText="@user.FullName" - SecondaryText="@user.DisplayName" - Presence="BitPersonaPresence.Online" /> - </div> - - <div class="form-input-container"> - @if (user.ProfileImageName is not null) - { - <BitButton IsLoading="isRemoving" OnClick="RemoveProfileImage" Color="BitColor.Error"> - @Localizer[nameof(AppStrings.Remove)] - </BitButton> - } - - <BitLabel>@Localizer[nameof(AppStrings.ProfileImage)]</BitLabel> - <BitFileUpload @onfocus="() => profileImageError = null" - Label="@Localizer[nameof(AppStrings.UploadNewProfileImage)]" - Accept="image/*" - AutoUploadEnabled="true" - MaxSize="1024 * 1024 * 10" - ChunkedUploadEnabled="false" - UploadUrl="@profileImageUploadUrl" - OnUploading="() => isUploading = true" - OnUploadComplete="WrapHandled(HandleOnUploadComplete)" - OnUploadFailed="() => profileImageError = Localizer[nameof(AppStrings.FileUploadFailed)]" /> - @if (isUploading) - { - <BitRingLoading /> - } - <div class="form-input-error">@profileImageError</div> - </div> - - <EditForm Model="editUserDto" OnValidSubmit="WrapHandled(SaveProfile)" class="edit-profile-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="editUserDto.FullName" - Label="@Localizer[nameof(AppStrings.FullName)]" - Placeholder="@Localizer[nameof(AppStrings.FullName)]" /> - <ValidationMessage For="@(() => editUserDto.FullName)" /> - </div> - - <div class="form-input-container"> - <BitDatePicker IsResponsive @bind-Value="editUserDto.BirthDate" - Class="edit-profile-dtp" - Label="@Localizer[nameof(AppStrings.BirthDate)]" - GoToTodayTitle="@Localizer[nameof(AppStrings.GoToToday)]" - Placeholder="@Localizer[nameof(AppStrings.SelectBirthDate)]" /> - <ValidationMessage For="@(() => editUserDto.BirthDate)" /> - </div> - - <div class="form-input-container"> - <BitChoiceGroup Horizontal - @bind-Value="editUserDto.GenderAsString" - TItem="BitChoiceGroupOption<string>" TValue="string" - Label="@Localizer[nameof(AppStrings.Gender)]"> - <BitChoiceGroupOption Value="@Gender.Male.ToString()" - Text="@Localizer[nameof(AppStrings.GenderMale)]" /> - <BitChoiceGroupOption Value="@Gender.Female.ToString()" - Text="@Localizer[nameof(AppStrings.GenderFemale)]" /> - <BitChoiceGroupOption Value="@Gender.Other.ToString()" - Text="@Localizer[nameof(AppStrings.GenderOther)]" /> - </BitChoiceGroup> - </div> - - <BitButton IsLoading="isSaving" - ButtonType="BitButtonType.Submit" - Title="@Localizer[nameof(AppStrings.Save)]"> - @Localizer[nameof(AppStrings.Save)] - </BitButton> - </EditForm> - } -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.scss deleted file mode 100644 index b74859c0de..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserDataSection.razor.scss +++ /dev/null @@ -1,7 +0,0 @@ -::deep .edit-profile-form { - width: 100%; - display: flex; - align-items: center; - flex-flow: column nowrap; - justify-content: flex-start; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor deleted file mode 100644 index 23a6e1ece5..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/Profile/UserSessionsSection.razor +++ /dev/null @@ -1,64 +0,0 @@ -@inherits AppComponentBase - -<div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Multiline Color="messageColor" OnDismiss="() => message = null">@message</BitMessage> - } -</div> - -<BitText Typography="BitTypography.H2" Style="text-align:center"> - @Localizer[nameof(AppStrings.UserSessionsTitle)] -</BitText> - -<br /> - -@if (isLoading) -{ - <div class="loading-container"> - <BitRingLoading /> - </div> -} -else -{ - <BitStack> - @if (currentSession is not null) - { - <BitStack> - <BitSeparator Style="width:100%">@Localizer[nameof(AppStrings.CurrentSession)]</BitSeparator> - <BitPersona PrimaryText="@currentSession.Device" - SecondaryText="@currentSession.Address" - TertiaryText="@($"{currentSession.IP} - {currentSession.LastSeenOn}")" - Size="BitPersonaSize.Size72" - Style="width:100%;padding:8px" - Presence="@GetPresence(currentSession.LastSeenOn)" - Class="@BitCss.Class.Color.Background.Secondary" - Styles="@(new() { Image = "width:50%;height:50%" })" - ImageInitials="✓" - ImageUrl="@($"/_content/Boilerplate.Client.Core/images/os/{GetImageUrl(currentSession.Device)}")" /> - </BitStack> - } - @if (otherSessions is not null && otherSessions.Any()) - { - <BitSeparator Style="width:100%">@Localizer[nameof(AppStrings.OtherSessions)]</BitSeparator> - - @foreach (var session in otherSessions) - { - <BitStack Horizontal Style="padding:8px" Class="@BitCss.Class.Color.Background.Secondary"> - <BitPersona PrimaryText="@session.Device" - SecondaryText="@session.Address" - TertiaryText="@($"{session.IP} - {session.LastSeenOn}")" - Size="BitPersonaSize.Size72" - Style="flex-grow:1" - Presence="@GetPresence(session.LastSeenOn)" - Styles="@(new() { Image = "width:50%;height:50%" })" - ImageInitials="@(session.IsValid ? "✓" : "✘")" - ImageUrl="@($"/_content/Boilerplate.Client.Core/images/os/{GetImageUrl(session.Device)}")" /> - <BitButton IconName="@(isWaiting ? BitIconName.CloudUpload : BitIconName.Delete)" - OnClick="() => RevokeSession(session)" - Variant="BitVariant.Text" /> - </BitStack> - } - } - </BitStack> -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor index a8abce73e4..731f87cb61 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor @@ -1,105 +1,154 @@ @attribute [Route(Urls.ResetPasswordPage)] @attribute [Route("{culture?}" + Urls.ResetPasswordPage)] -@inherits AppComponentBase +@inherits AppPageBase -<PageTitle>@Localizer[nameof(AppStrings.ResetPassword)]</PageTitle> +<PageTitle>@Localizer[nameof(AppStrings.ResetPasswordPageTitle)]</PageTitle> -<div class="page-container"> - <div class="form"> - @if (string.IsNullOrEmpty(errorMessage) is false) - { - <BitMessage Multiline Class="form-message-bar" Color="BitColor.Error" OnDismiss="() => errorMessage = null"> - @errorMessage - </BitMessage> - } - - <BitText Typography="BitTypography.H4" Gutter> - @Localizer[nameof(AppStrings.ResetPasswordTitle)] - </BitText> +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitText Typography="BitTypography.H4" Gutter>@Localizer[nameof(AppStrings.ResetPasswordTitle)]</BitText> @if (isPasswordChanged is false) { - <BitText Typography="BitTypography.Subtitle1" Gutter> + <BitText Typography="BitTypography.Subtitle1" Align="BitTextAlign.Center"> @Localizer[nameof(AppStrings.ResetPasswordSubtitle)] - <br /> @Localizer[nameof(AppStrings.ResetPasswordMessage)] </BitText> <br /> - <EditForm Model="model" OnValidSubmit="WrapHandled(Submit)" class="reset-form"> + <EditForm Model="model" OnValidSubmit="WrapHandled(Submit)" novalidate> <AppDataAnnotationsValidator /> - @if (showEmail) - { - <div class="form-input-container"> - <BitTextField @bind-Value="model.Email" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.Email)]" - IsEnabled="string.IsNullOrEmpty(EmailQueryString)" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => model.Email)" /> - </div> - } + <BitStack FillContent Gap="2rem"> + @if (isTokenEntered is false) + { + <BitStack FillContent> + @if (showEmail && showPhone) + { + <BitPivot Alignment="BitAlignment.Center" SelectedKey="@selectedKey" SelectedKeyChanged="OnSelectedKeyChanged"> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]" Key="@EmailKey"> + <BitTextField @bind-Value="model.Email" + AutoFocus TabIndex="1" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.Email)]" + IsEnabled="string.IsNullOrEmpty(EmailQueryString)" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => model.Email)" /> + </BitPivotItem> - @if (showPhone) - { - <div class="form-input-container"> - <BitTextField @bind-Value="model.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - IsEnabled="string.IsNullOrEmpty(PhoneNumberQueryString)" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => model.PhoneNumber)" /> - </div> - } + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Phone)]" Key="@PhoneKey"> + <BitTextField @bind-Value="model.PhoneNumber" + AutoFocus TabIndex="1" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + IsEnabled="string.IsNullOrEmpty(PhoneNumberQueryString)" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => model.PhoneNumber)" /> + </BitPivotItem> + </BitPivot> + } + else if (showEmail) + { + <BitTextField @bind-Value="model.Email" + AutoFocus TabIndex="1" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.Email)]" + IsEnabled="string.IsNullOrEmpty(EmailQueryString)" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => model.Email)" /> + } + else if (showPhone) + { + <BitTextField @bind-Value="model.PhoneNumber" + AutoFocus TabIndex="1" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + IsEnabled="string.IsNullOrEmpty(PhoneNumberQueryString)" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => model.PhoneNumber)" /> + } - <div class="form-input-container"> - <BitTextField @bind-Value="model.Token" - Type="BitInputType.Number" - Label="@Localizer[nameof(AppStrings.Token)]" - Placeholder="@Localizer[nameof(AppStrings.TokenPlaceholder)]" /> - <ValidationMessage For="@(() => model.Token)" /> - </div> + <BitTextField @bind-Value="model.Token" + TabIndex="2" + Type="BitInputType.Number" + Label="@Localizer[nameof(AppStrings.Token)]" + Placeholder="@Localizer[nameof(AppStrings.TokenPlaceholder)]" /> + <ValidationMessage For="@(() => model.Token)" /> + </BitStack> - <div class="form-input-container"> - <BitTextField @bind-Value="model.Password" - CanRevealPassword="true" - AutoComplete="new-password" - Type="BitInputType.Password" - Label="@Localizer[nameof(AppStrings.Password)]" - Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]" /> - <ValidationMessage For="@(() => model.Password)" /> - </div> + <BitButton ButtonType="BitButtonType.Button" OnClick="WrapHandled(HandleContinue)"> + @Localizer[nameof(AppStrings.Continue)] + </BitButton> - <div class="form-input-container"> - <BitTextField @bind-Value="model.ConfirmPassword" - CanRevealPassword="true" - AutoComplete="new-password" - Type="BitInputType.Password" - Label="@Localizer[nameof(AppStrings.ConfirmPassword)]" - Placeholder="@Localizer[nameof(AppStrings.ConfirmPassword)]" /> - <ValidationMessage For="@(() => model.ConfirmPassword)" /> - </div> + @if (selectedKey == EmailKey) + { + <BitText Typography="BitTypography.Body1" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.NotReceivedEmailMessage)] + <br /> + @Localizer[nameof(AppStrings.CheckSpamMailMessage)] + </BitText> + } + else + { + <BitText Typography="BitTypography.Body1" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.NotReceivedPhoneMessage)] + </BitText> + } - <br /> + <BitButton AutoLoading + Variant="BitVariant.Outline" + OnClick="WrapHandled(Resend)" + ButtonType="BitButtonType.Button"> + @Localizer[nameof(AppStrings.Resend)] + </BitButton> + } + else + { + <BitStack FillContent> + <div style="min-height:80px"> + <BitTextField @bind-Value="model.Password" + AutoFocus TabIndex="1" + CanRevealPassword="true" + AutoComplete="new-password" + Type="BitInputType.Password" + Label="@Localizer[nameof(AppStrings.Password)]" + Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]" /> + <ValidationMessage For="@(() => model.Password)" /> + </div> + <div style="min-height:80px"> + <BitTextField @bind-Value="model.ConfirmPassword" + TabIndex="2" + CanRevealPassword="true" + AutoComplete="new-password" + Type="BitInputType.Password" + Label="@Localizer[nameof(AppStrings.ConfirmPassword)]" + Placeholder="@Localizer[nameof(AppStrings.ConfirmPassword)]" /> + <ValidationMessage For="@(() => model.ConfirmPassword)" /> + </div> + </BitStack> - <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.ResetPasswordButtonText)] - </BitButton> + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.ResetPasswordButtonText)] + </BitButton> + } + </BitStack> </EditForm> } else { - <BitText Typography="BitTypography.H5" Gutter> + <BitText Typography="BitTypography.H5"> @Localizer[nameof(AppStrings.ResetPasswordSuccessTitle), model.PhoneNumber!] </BitText> - <br /> - <BitText Typography="BitTypography.Subtitle1" Gutter> + + <BitText Typography="BitTypography.Subtitle1"> @Localizer[nameof(AppStrings.ResetPasswordSuccessBody)] </BitText> - <br /> - <BitLink Href="@Urls.SignInPage"> - @Localizer[nameof(AppStrings.SignIn)] - </BitLink> } - </div> -</div> + <br /> + <BitStack Horizontal HorizontalAlign="BitAlignment.Center"> + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + <BitText>@Localizer[nameof(AppStrings.Or)]</BitText> + <BitLink Href="@Urls.SignUpPage">@Localizer[nameof(AppStrings.SignUp)]</BitLink> + </BitStack> + + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.cs index 29a3a0c0da..35c0645e04 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.cs @@ -5,23 +5,31 @@ namespace Boilerplate.Client.Core.Components.Pages.Identity; public partial class ResetPasswordPage { + [Parameter, SupplyParameterFromQuery(Name = "email")] + public string? EmailQueryString { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "phoneNumber")] + public string? PhoneNumberQueryString { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "token")] + public string? TokenQueryString { get; set; } + + [AutoInject] IIdentityController identityController = default!; + private bool isWaiting; private bool showEmail; private bool showPhone; + private bool isTokenEntered; private bool isPasswordChanged; - private string? errorMessage; private ResetPasswordRequestDto model = new(); - [Parameter, SupplyParameterFromQuery(Name = "email")] - public string? EmailQueryString { get; set; } + private const string EmailKey = nameof(EmailKey); + private const string PhoneKey = nameof(PhoneKey); - [Parameter, SupplyParameterFromQuery(Name = "phoneNumber")] - public string? PhoneNumberQueryString { get; set; } + private string selectedKey = EmailKey; - [Parameter, SupplyParameterFromQuery(Name = "token")] - public string? TokenQueryString { get; set; } protected override async Task OnInitAsync() { @@ -42,15 +50,39 @@ protected override async Task OnInitAsync() showEmail = showPhone = true; } + HandleContinue(); + await base.OnInitAsync(); } + private void OnSelectedKeyChanged(string key) + { + selectedKey = key; + + if (key == EmailKey) + { + model.PhoneNumber = null; + } + + if (key == PhoneKey) + { + model.Email = null; + } + } + + private void HandleContinue() + { + if (string.IsNullOrEmpty(model.Token)) return; + if (string.IsNullOrEmpty(model.Email) && string.IsNullOrEmpty(model.PhoneNumber)) return; + + isTokenEntered = true; + } + private async Task Submit() { if (isWaiting) return; isWaiting = true; - errorMessage = null; try { @@ -60,7 +92,31 @@ private async Task Submit() } catch (KnownException e) { - errorMessage = e.Message; + SnackBarService.Error(e.Message); + } + finally + { + isWaiting = false; + } + } + + private async Task Resend() + { + if (isWaiting) return; + + isWaiting = true; + + try + { + var resendModel = new SendResetPasswordTokenRequestDto { Email = model.Email, PhoneNumber = model.PhoneNumber }; + + await identityController.SendResetPasswordToken(resendModel, CurrentCancellationToken); + + SnackBarService.Success(Localizer[nameof(AppStrings.SuccessfulSendTokenMessage)]); + } + catch (KnownException e) + { + SnackBarService.Error(e.Message); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.scss index 5bade4edc3..0fb1d9708a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/ResetPasswordPage.razor.scss @@ -1,18 +1,12 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; +@import '../../../Styles/abstracts/_media-queries.scss'; -.page-container { +section { width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; } -::deep .reset-form { - width: 100%; - display: flex; - flex-flow: column; - align-items: center; +::deep { + form { + width: 304px; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor new file mode 100644 index 0000000000..f1871e4803 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor @@ -0,0 +1,59 @@ +@inherits AppComponentBase + +@{ + var hasEmail = Model.Email is not null; + var emailOrPhone = Localizer[(hasEmail ? nameof(AppStrings.Email) : nameof(AppStrings.PhoneNumber))]; +} + +<section> + <BitStack HorizontalAlign="BitAlignment.Center" FillContent Gap="2rem"> + <BitStack> + <BitText Typography="BitTypography.H4">@(string.Format(Localizer[nameof(AppStrings.OtpPanelTitle)], emailOrPhone))</BitText> + <BitText Typography="BitTypography.Subtitle1" Color="BitColor.SecondaryForeground"> + @(string.Format(Localizer[nameof(AppStrings.OtpPanelSubtitle)], emailOrPhone, hasEmail ? Model.Email : Model.PhoneNumber)) + </BitText> + </BitStack> + + <BitStack FillContent Gap="2rem"> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitOtpInput @bind-Value="Model.Otp" + AutoFocus + Length="6" + Size="BitSize.Large" + Type="BitInputType.Number" + Style="justify-content:center" + Label="@Localizer[nameof(AppStrings.Code)]" + OnFill="WrapHandled(async (string? otp) => await OnSignIn.InvokeAsync(otp))" /> + <ValidationMessage For="@(() => Model.Otp)" /> + </BitStack> + + <BitButton IsLoading="IsWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Continue)] + </BitButton> + + <BitText Typography="BitTypography.Body2"> + @Localizer[nameof(AppStrings.OtpResendMessage)] + <BitLink OnClick="WrapHandled(async () => await OnResendOtp.InvokeAsync())">@Localizer[nameof(AppStrings.Resend)]</BitLink> + </BitText> + + @if (hasEmail) + { + <BitStack Horizontal Gap="2rem" HorizontalAlign="BitAlignment.Center"> + <BitLink Href="https://www.gmail.com"> + <BitStack Horizontal> + <BitImage Src="_content/Boilerplate.Client.Core/images/icons/gmail-icon.png" /> + <BitText>Open Gmail</BitText> + </BitStack> + </BitLink> + + <BitLink Href="https://www.outlook.com"> + <BitStack Horizontal> + <BitImage Src="_content/Boilerplate.Client.Core/images/icons/outlook-icon.png" /> + <BitText>Open Outlook</BitText> + </BitStack> + </BitLink> + </BitStack> + } + </BitStack> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.cs new file mode 100644 index 0000000000..aad7e32be2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.cs @@ -0,0 +1,14 @@ +using Boilerplate.Shared.Dtos.Identity; + +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignIn; + +public partial class OtpPanel +{ + [Parameter] public bool IsWaiting { get; set; } + + [Parameter] public SignInRequestDto Model { get; set; } = default!; + + [Parameter] public EventCallback<string?> OnSignIn { get; set; } + + [Parameter] public EventCallback OnResendOtp { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.scss new file mode 100644 index 0000000000..a7782bf36b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/OtpPanel.razor.scss @@ -0,0 +1,4 @@ +section { + width: 100%; + height: 100%; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor new file mode 100644 index 0000000000..0536df2943 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor @@ -0,0 +1,32 @@ +@*+:cnd:noEmit*@ +@attribute [Route(Urls.SignInPage)] +@attribute [Route("{culture?}" + Urls.SignInPage)] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.SignInPageTitle)]</PageTitle> + +<section> + <BitStack HorizontalAlign="BitAlignment.Center" FillContent> + <EditForm Model="model" OnValidSubmit="WrapHandled(DoSignIn)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack HorizontalAlign="BitAlignment.Center"> + @if (requiresTwoFactor is false) + { + @if (isOtpSent is false) + { + <SignInPanel IsWaiting="isWaiting" Model="model" OnSocialSignIn="SocialSignIn" OnSendOtp="SendOtp" /> + } + else + { + <OtpPanel IsWaiting="isWaiting" Model="model" OnSignIn="DoSignIn" OnResendOtp="ResendOtp" /> + } + } + else + { + <TfaPanel IsWaiting="isWaiting" Model="model" OnSendTfaToken="SendTfaToken" /> + } + </BitStack> + </EditForm> + </BitStack> +</section> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs similarity index 52% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs index dc05e64b16..0cc06ee91e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs @@ -1,28 +1,12 @@ //+:cnd:noEmit -using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Dtos.Identity; +using Microsoft.AspNetCore.Components.Forms; -namespace Boilerplate.Client.Core.Components.Pages.Identity; +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignIn; -public partial class SignInPage +public partial class SignInPage : IDisposable { - private bool isWaiting; - private bool isSendingOtp; - private bool requiresTwoFactor; - private bool isSendingTfaToken; - private readonly SignInRequestDto model = new(); - - private string? message; - private BitColor messageColor; - private BitOtpInput otpInputRef = default!; - private ElementReference messageRef = default!; - - - [AutoInject] private ILocalHttpServer localHttpServer = default!; - [AutoInject] private IIdentityController identityController = default!; - [AutoInject] private IExternalNavigationService externalNavigationService = default!; - - [Parameter, SupplyParameterFromQuery(Name = "return-url")] public string? ReturnUrlQueryString { get; set; } @@ -42,6 +26,23 @@ public partial class SignInPage public string? ErrorQueryString { get; set; } + [AutoInject] private ILocalHttpServer localHttpServer = default!; + [AutoInject] private ITelemetryContext telemetryContext = default!; + [AutoInject] private IIdentityController identityController = default!; + [AutoInject] private IExternalNavigationService externalNavigationService = default!; + + + private bool isWaiting; + private bool isOtpSent; + private bool requiresTwoFactor; + private readonly SignInRequestDto model = new(); + private Action unsubscribeIdentityHeaderBackLinkClicked = default!; + + + private const string OtpPayload = nameof(OtpPayload); + private const string TfaPayload = nameof(TfaPayload); + + protected override async Task OnInitAsync() { await base.OnInitAsync(); @@ -49,7 +50,7 @@ protected override async Task OnInitAsync() model.UserName = UserNameQueryString; model.Email = EmailQueryString; model.PhoneNumber = PhoneNumberQueryString; - model.DeviceInfo = AppPlatform.OSDescription; + model.DeviceInfo = telemetryContext.OS; if (string.IsNullOrEmpty(OtpQueryString) is false) { @@ -66,24 +67,58 @@ protected override async Task OnInitAsync() if (string.IsNullOrEmpty(ErrorQueryString) is false) { - message = ErrorQueryString; - messageColor = BitColor.Error; + SnackBarService.Error(ErrorQueryString); + } + + unsubscribeIdentityHeaderBackLinkClicked = PubSubService.Subscribe(ClientPubSubMessages.IDENTITY_HEADER_BACK_LINK_CLICKED, async payload => + { + var source = (string?)payload; + + if (source == OtpPayload) + { + isOtpSent = false; + model.Otp = null; + } + + if (source == TfaPayload) + { + requiresTwoFactor = false; + model.TwoFactorCode = null; + } + + await InvokeAsync(StateHasChanged); + + PubSubService.Publish(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, null); + }); + } + + + private async Task SocialSignIn(string provider) + { + try + { + var port = localHttpServer.Start(CurrentCancellationToken); + + var redirectUrl = await identityController.GetSocialSignInUri(provider, ReturnUrlQueryString, port is -1 ? null : port, CurrentCancellationToken); + + await externalNavigationService.NavigateToAsync(redirectUrl); + } + catch (KnownException e) + { + SnackBarService.Error(e.Message); } } private async Task DoSignIn() { if (isWaiting) return; + if (isOtpSent && string.IsNullOrWhiteSpace(model.Otp)) return; isWaiting = true; - message = null; try { - if (requiresTwoFactor && - string.IsNullOrWhiteSpace(model.TwoFactorCode) && - string.IsNullOrWhiteSpace(model.TwoFactorRecoveryCode) && - string.IsNullOrWhiteSpace(model.TwoFactorToken)) return; + if (requiresTwoFactor && string.IsNullOrWhiteSpace(model.TwoFactorCode)) return; requiresTwoFactor = await AuthenticationManager.SignIn(model, CurrentCancellationToken); @@ -91,12 +126,14 @@ private async Task DoSignIn() { NavigationManager.NavigateTo(ReturnUrlQueryString ?? Urls.HomePage, replace: true); } + else + { + PubSubService.Publish(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, TfaPayload); + } } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(e.Message); } finally { @@ -104,106 +141,54 @@ private async Task DoSignIn() } } - private async Task SendOtp() - { - if (isSendingOtp) return; + private Task ResendOtp() => SendOtp(true); + private Task SendOtp() => SendOtp(false); - isSendingOtp = true; - message = null; + private async Task SendOtp(bool resend) + { + if (model.Email is null && model.PhoneNumber is null) return; - try + if (model.Email is not null && new EmailAddressAttribute().IsValid(model.Email) is false) { - var request = new IdentityRequestDto { UserName = model.UserName, Email = model.Email, PhoneNumber = model.PhoneNumber }; - await identityController.SendOtp(request, ReturnUrlQueryString, CurrentCancellationToken); - - message = Localizer[nameof(AppStrings.OtpSentMessage)]; - messageColor = BitColor.Success; - await messageRef.ScrollIntoView(); + SnackBarService.Error(string.Format(AppStrings.EmailAddressAttribute_ValidationError, AppStrings.Email)); + return; } - catch (KnownException e) + + if (model.PhoneNumber is not null && new PhoneAttribute().IsValid(model.PhoneNumber) is false) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); + SnackBarService.Error(string.Format(AppStrings.PhoneAttribute_ValidationError, AppStrings.PhoneNumber)); + return; } - finally + + var request = new IdentityRequestDto { UserName = model.UserName, Email = model.Email, PhoneNumber = model.PhoneNumber }; + + _ = identityController.SendOtp(request, ReturnUrlQueryString, CurrentCancellationToken); + + if (resend is false) { - isSendingOtp = false; - await otpInputRef.FocusAsync(); + isOtpSent = true; + + PubSubService.Publish(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, OtpPayload); } } private async Task SendTfaToken() { - if (isSendingTfaToken) return; - - isSendingTfaToken = true; - message = null; - try { await identityController.SendTwoFactorToken(model, CurrentCancellationToken); - message = Localizer[nameof(AppStrings.TfaTokenSentMessage)]; - messageColor = BitColor.Success; - await messageRef.ScrollIntoView(); + SnackBarService.Success(Localizer[nameof(AppStrings.TfaTokenSentMessage)]); } catch (KnownException e) { - message = e.Message; - messageColor = BitColor.Error; - await messageRef.ScrollIntoView(); - } - finally - { - isSendingTfaToken = false; + SnackBarService.Error(e.Message); } } - private async Task GoogleSignIn() - { - await SocialSignIn("Google"); - } - - private async Task GitHubSignIn() - { - await SocialSignIn("GitHub"); - } - - private async Task TwitterSignIn() - { - await SocialSignIn("Twitter"); - } - private async Task SocialSignIn(string provider) + public void Dispose() { - if (isWaiting) return; - - isWaiting = true; - message = null; - - try - { - var port = localHttpServer.Start(CurrentCancellationToken); - - var redirectUrl = await identityController.GetSocialSignInUri(provider, ReturnUrlQueryString, port is -1 ? null : port, CurrentCancellationToken); - - await externalNavigationService.NavigateToAsync(redirectUrl); - } - catch (KnownException e) - { - isWaiting = false; - - message = e.Message; - messageColor = BitColor.Error; - - await messageRef.ScrollIntoView(); - } - catch - { - isWaiting = false; - - throw; - } + unsubscribeIdentityHeaderBackLinkClicked?.Invoke(); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.scss new file mode 100644 index 0000000000..c1250519ac --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.scss @@ -0,0 +1,23 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: 100%; + height: 100%; +} + +.form-forgot-password { + font-size: 14px; + line-height: 24px; + margin-bottom: 20px; +} + +.tfa-otp-container { + gap: 4px; +} + + +::deep { + form { + max-width: 460px; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor new file mode 100644 index 0000000000..28620fea49 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor @@ -0,0 +1,81 @@ +@inherits AppComponentBase + +<section> + <BitStack HorizontalAlign="BitAlignment.Center" FillContent> + <BitStack> + <BitText Typography="BitTypography.H4">@Localizer[nameof(AppStrings.SignInPanelTitle)]</BitText> + + <BitText Typography="BitTypography.Subtitle1" Color="BitColor.SecondaryForeground"> + @Localizer[nameof(AppStrings.SignInPanelSubtitle)] + </BitText> + </BitStack> + + <SocialRow OnClick="OnSocialSignIn" /> + + <BitSeparator Border="BitColorKind.Tertiary" Background="BitColorKind.Secondary" Class="lg-sep">@Localizer[AppStrings.Or]</BitSeparator> + <BitSeparator Border="BitColorKind.Secondary" Background="BitColorKind.Primary" Class="sm-sep">@Localizer[AppStrings.Or]</BitSeparator> + + <BitStack FillContent Gap="2rem"> + <BitStack FillContent> + <BitPivot Alignment="BitAlignment.Center" SelectedKey="@selectedKey" SelectedKeyChanged="OnSelectedKeyChanged"> + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]" Key="@EmailKey"> + <BitTextField @bind-Value="Model.Email" + AutoFocus TabIndex="1" + Type="BitInputType.Email" + Immediate DebounceTime="500" + Label="@Localizer[nameof(AppStrings.Email)]" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => Model.Email)" /> + </BitPivotItem> + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.PhoneNumber)]" Key="@PhoneKey"> + <BitTextField @bind-Value="Model.PhoneNumber" + AutoFocus TabIndex="1" + Type="BitInputType.Tel" + Immediate DebounceTime="500" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => Model.PhoneNumber)" /> + </BitPivotItem> + </BitPivot> + + <BitStack Gap="0" FillContent> + <BitTextField @bind-Value="Model.Password" + TabIndex="2" + CanRevealPassword="true" + Type="BitInputType.Password" + AutoComplete="@BitAutoCompleteValue.CurrentPassword" + Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]"> + <LabelTemplate> + <BitStack Horizontal VerticalAlign="BitAlignment.Center"> + <BitText>@Localizer[nameof(AppStrings.Password)]</BitText> + <BitSpacer /> + <BitLink Href="@Urls.ForgotPasswordPage">@Localizer[nameof(AppStrings.ForgotPasswordLink)]</BitLink> + </BitStack> + </LabelTemplate> + </BitTextField> + <ValidationMessage For="@(() => Model.Password)" /> + </BitStack> + </BitStack> + + <BitCheckbox @bind-Value="Model.RememberMe" Label="@Localizer[nameof(AppStrings.RememberMe)]" /> + + <BitButton IsLoading="IsWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.SignIn)] + </BitButton> + + <BitButton AutoLoading + Variant="BitVariant.Outline" + ButtonType="BitButtonType.Button" + OnClick="WrapHandled(async () => await OnSendOtp.InvokeAsync())" + IsEnabled="@(Model.Email is not null || Model.PhoneNumber is not null)"> + @Localizer[nameof(AppStrings.SendOtpButtonText)] + </BitButton> + + <BitText Align="BitTextAlign.Center" Typography="BitTypography.Body2"> + @Localizer[nameof(AppStrings.DontHaveAccountMessage)] + <BitLink Href="@Urls.SignUpPage">@Localizer[nameof(AppStrings.SignUp)]</BitLink> + </BitText> + </BitStack> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.cs new file mode 100644 index 0000000000..d8774e71ce --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.cs @@ -0,0 +1,35 @@ +using Boilerplate.Shared.Dtos.Identity; + +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignIn; + +public partial class SignInPanel +{ + [Parameter] public bool IsWaiting { get; set; } + + [Parameter] public SignInRequestDto Model { get; set; } = default!; + + [Parameter] public EventCallback<string?> OnSocialSignIn { get; set; } + + [Parameter] public EventCallback OnSendOtp { get; set; } + + + private const string EmailKey = nameof(EmailKey); + private const string PhoneKey = nameof(PhoneKey); + + private string selectedKey = EmailKey; + + private void OnSelectedKeyChanged(string key) + { + selectedKey = key; + + if (key == EmailKey) + { + Model.PhoneNumber = null; + } + + if (key == PhoneKey) + { + Model.Email = null; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.scss new file mode 100644 index 0000000000..1baca747b3 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.scss @@ -0,0 +1,20 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: 100%; + height: 100%; +} + +::deep { + .lg-sep { + @include lt-md { + display: none; + } + } + + .sm-sep { + @include gt-sm { + display: none; + } + } +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor new file mode 100644 index 0000000000..a3bc9fbd65 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor @@ -0,0 +1,35 @@ +@inherits AppComponentBase + +<section> + <BitStack HorizontalAlign="BitAlignment.Center" FillContent Gap="2rem"> + <BitText Typography="BitTypography.H4">@Localizer[nameof(AppStrings.TfaPanelTitle)]</BitText> + + <BitText Color="BitColor.SecondaryForeground">@Localizer[nameof(AppStrings.TfaPanelSubtitle)]</BitText> + + <BitTextField @bind-Value="Model.TwoFactorCode" + Label="@Localizer[nameof(AppStrings.Code)]" + AutoComplete="@BitAutoCompleteValue.OneTimeCode" + Placeholder="@Localizer[nameof(AppStrings.TwoFactorCode)]" /> + + <BitButton IsLoading="IsWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.Continue)] + </BitButton> + + <BitCard Border="BitColorKind.Secondary" FullWidth> + <BitStack Horizontal> + <BitIcon IconName="@BitIconName.SkypeCircleCheck" Color="BitColor.Info" /> + <BitStack> + <BitText>@Localizer[nameof(AppStrings.TfaPanelAnotherWayTitle)]</BitText> + <BitText Color="BitColor.SecondaryForeground">@Localizer[nameof(AppStrings.TfaPanelAnotherWaySubtitle)]</BitText> + <BitButton AutoLoading + Color="BitColor.Tertiary" + Variant="BitVariant.Outline" + ButtonType="BitButtonType.Button" + OnClick="WrapHandled(async () => await OnSendTfaToken.InvokeAsync())"> + @Localizer[nameof(AppStrings.TfaPanelAnotherWayGetCode)] + </BitButton> + </BitStack> + </BitStack> + </BitCard> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.cs new file mode 100644 index 0000000000..83df2ce9ef --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.cs @@ -0,0 +1,12 @@ +using Boilerplate.Shared.Dtos.Identity; + +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignIn; + +public partial class TfaPanel +{ + [Parameter] public bool IsWaiting { get; set; } + + [Parameter] public SignInRequestDto Model { get; set; } = default!; + + [Parameter] public EventCallback OnSendTfaToken { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.scss new file mode 100644 index 0000000000..a7782bf36b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/TfaPanel.razor.scss @@ -0,0 +1,4 @@ +section { + width: 100%; + height: 100%; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor deleted file mode 100644 index f1701b2ae7..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor +++ /dev/null @@ -1,210 +0,0 @@ -@*+:cnd:noEmit*@ -@attribute [Route(Urls.SignInPage)] -@attribute [Route("{culture?}" + Urls.SignInPage)] -@inherits AppComponentBase -@using Boilerplate.Client.Core.Components.Pages.Identity.Components - -<PageTitle>@Localizer[nameof(AppStrings.SignInTitle)]</PageTitle> - -<div class="page-container"> - <EditForm OnValidSubmit="WrapHandled(DoSignIn)" class="form" Model="model"> - <AppDataAnnotationsValidator /> - - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Multiline Color="messageColor" OnDismiss="() => message = null">@message</BitMessage> - } - </div> - - <BitText Typography="BitTypography.H4" Gutter> - @Localizer[nameof(AppStrings.SignIn)] - </BitText> - - @if (requiresTwoFactor is false) - { - <br /><br /> - <BitStack Horizontal> - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(GoogleSignIn)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.GoogleSignUpButtonText]"> - <GoogleIcon /> - </BitButton> - - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(GitHubSignIn)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.GitHubSignUpButtonText]"> - <GitHubIcon /> - </BitButton> - - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(TwitterSignIn)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.TwitterSignUpButtonText]"> - <TwitterIcon /> - </BitButton> - </BitStack> - <br /> - <BitSeparator Style="width:100%">@Localizer[AppStrings.Or]</BitSeparator> - <br /> - <BitPivot Style="width:100%;max-width:300px"> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]"> - <br /> - <div class="form-input-container"> - <BitTextField @bind-Value="model.Email" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.Email)]" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => model.Email)" /> - </div> - <div class="form-input-container"> - <BitTextField @bind-Value="model.Password" - CanRevealPassword="true" - Type="BitInputType.Password" - AutoComplete="@BitAutoCompleteValue.CurrentPassword" - Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]"> - <LabelTemplate> - <BitStack Horizontal> - <BitText>@Localizer[nameof(AppStrings.Password)]</BitText> - <BitSpacer /> - <BitLink Href="@Urls.ForgotPasswordPage">@Localizer[nameof(AppStrings.ForgotPasswordLink)]</BitLink> - </BitStack> - </LabelTemplate> - </BitTextField> - <ValidationMessage For="@(() => model.Password)" /> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.PhoneNumber)]"> - <br /> - <div class="form-input-container"> - <BitTextField @bind-Value="model.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => model.PhoneNumber)" /> - </div> - <div class="form-input-container"> - <BitTextField @bind-Value="model.Password" - CanRevealPassword="true" - Type="BitInputType.Password" - Label="@Localizer[nameof(AppStrings.Password)]" - AutoComplete="@BitAutoCompleteValue.CurrentPassword" - Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]"> - <LabelTemplate> - <BitStack Horizontal> - <BitText>@Localizer[nameof(AppStrings.Password)]</BitText> - <BitSpacer /> - <BitLink Href="@Urls.ForgotPasswordPage">@Localizer[nameof(AppStrings.ForgotPasswordLink)]</BitLink> - </BitStack> - </LabelTemplate> - </BitTextField> - <ValidationMessage For="@(() => model.Password)" /> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Otp)]"> - <br /> - <div class="form-input-container tfa-otp-container"> - <BitText>@Localizer[nameof(AppStrings.OtpEmailOrPhoneNumberSubtitle)]</BitText> - <BitTextField @bind-Value="model.Email" - Type="BitInputType.Email" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <BitTextField @bind-Value="model.PhoneNumber" - Type="BitInputType.Tel" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <BitButton IsLoading="isSendingOtp" - Variant="BitVariant.Outline" - OnClick="WrapHandled(SendOtp)" - ButtonType="BitButtonType.Button"> - @Localizer[nameof(AppStrings.SendOtpButtonText)] - </BitButton> - <br /> - <BitOtpInput Length="6" - @ref="otpInputRef" - @bind-Value="model.Otp" - Size="BitSize.Large" - Label="@Localizer[nameof(AppStrings.Otp)]" - Type="BitInputType.Number" - Style="justify-content:center" - OnFill="WrapHandled(DoSignIn)" /> - <ValidationMessage For="@(() => model.Otp)" /> - <br /> - </div> - </BitPivotItem> - </BitPivot> - <div class="form-input-container form-input-container--no-margin"> - <BitCheckbox Class="form-checkbox" @bind-Value="model.RememberMe" Label="@Localizer[nameof(AppStrings.RememberMe)]" /> - </div> - } - else - { - <BitText Typography="BitTypography.H4" Gutter>@Localizer[nameof(AppStrings.TwoFactorAuthTitle)]</BitText> - <BitText Gutter>@Localizer[nameof(AppStrings.TfaProtectedSignInSubtitle)]</BitText> - <br /> - <BitPivot> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TwoFactorCode)]"> - <br /> - <BitStack> - <div class="form-input-container tfa-pivot-item"> - <BitTextField @bind-Value="model.TwoFactorCode" - Label="@Localizer[nameof(AppStrings.TfaEnterCodeInSignInMessage)]" - AutoComplete="@BitAutoCompleteValue.OneTimeCode" - Placeholder="@Localizer[nameof(AppStrings.TwoFactorCode)]" /> - </div> - </BitStack> - <br /><br /> - </BitPivotItem> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TwoFactorRecoveryCode)]"> - <br /> - <BitStack> - <div class="form-input-container tfa-pivot-item"> - <BitTextField @bind-Value="model.TwoFactorRecoveryCode" - Label="@Localizer[nameof(AppStrings.TfaEnterRecoveryCodeInSignInMessage)]" - AutoComplete="@BitAutoCompleteValue.OneTimeCode" - Placeholder="@Localizer[nameof(AppStrings.TwoFactorRecoveryCode)]" /> - </div> - </BitStack> - <br /><br /> - </BitPivotItem> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.TwoFactorToken)]"> - <br /> - <BitStack> - <div class="form-input-container tfa-pivot-item"> - <div>@Localizer[nameof(AppStrings.TfaTokenSignInTitle)]</div> - <BitButton IsLoading="isSendingTfaToken" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(SendTfaToken)"> - @Localizer[nameof(AppStrings.TfaTokenGenerateButtonText)] - </BitButton> - <BitTextField @bind-Value="model.TwoFactorToken" Style="flex-grow:1" - AutoComplete="@BitAutoCompleteValue.OneTimeCode" - Placeholder="@Localizer[nameof(AppStrings.TwoFactorToken)]" /> - </div> - </BitStack> - </BitPivotItem> - </BitPivot> - } - <br /> - <BitButton IsLoading="isWaiting" - Class="form-submit-button" - ButtonType="BitButtonType.Submit" - Title="@Localizer[nameof(AppStrings.SignIn)]"> - @Localizer[nameof(AppStrings.SignIn)] - </BitButton> - <br /><br /> - <BitText Typography="BitTypography.Body2"> - @Localizer[nameof(AppStrings.DontHaveAccountMessage)] - <BitLink Href="@Urls.SignUpPage">@Localizer[nameof(AppStrings.SignUp)]</BitLink> - </BitText> - </EditForm> -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.scss deleted file mode 100644 index 5a14d3db4c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignInPage.razor.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; - -.page-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} - -.form-forgot-password { - font-size: rem2(14px); - line-height: rem2(24px); - margin-bottom: rem2(20px); -} - -.tfa-otp-container { - gap: 4px; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/GoogleRecaptcha.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/GoogleRecaptcha.razor similarity index 69% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/GoogleRecaptcha.razor rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/GoogleRecaptcha.razor index e39121fb96..e8bc9ce4d5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/GoogleRecaptcha.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/GoogleRecaptcha.razor @@ -1,8 +1,9 @@ @inherits AppComponentBase +@inject ClientCoreSettings clientCoreSettings <script type="text/javascript"> function onloadCallback() { grecaptcha.render('grecaptcha_element'); }; </script> <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit&hl=@(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)" async defer></script> -<div id="grecaptcha_element" data-sitekey="@(Configuration.GetRequiredValue<string>("GoogleRecaptchaSiteKey"))"></div> \ No newline at end of file +<div id="grecaptcha_element" data-sitekey="@(clientCoreSettings.GoogleRecaptchaSiteKey)"></div> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor new file mode 100644 index 0000000000..af6bc32736 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor @@ -0,0 +1,88 @@ +@*+:cnd:noEmit*@ +@attribute [Route(Urls.SignUpPage)] +@attribute [Route("{culture?}" + Urls.SignUpPage)] +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.SignUpPageTitle)]</PageTitle> + +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitStack AutoHeight> + <BitText Typography="BitTypography.H4">@Localizer[nameof(AppStrings.SignUpPanelTitle)]</BitText> + + <BitText Typography="BitTypography.Subtitle1" Color="BitColor.SecondaryForeground"> + @Localizer[nameof(AppStrings.SignUpPanelSubtitle)] + </BitText> + </BitStack> + + <SocialRow OnClick="SocialSignUp" /> + + <BitSeparator Border="BitColorKind.Tertiary" Background="BitColorKind.Secondary" Class="lg-sep">@Localizer[AppStrings.Or]</BitSeparator> + <BitSeparator Border="BitColorKind.Secondary" Background="BitColorKind.Primary" Class="sm-sep">@Localizer[AppStrings.Or]</BitSeparator> + + <EditForm Model="signUpModel" OnValidSubmit="WrapHandled(DoSignUp)" novalidate> + <AppDataAnnotationsValidator /> + + <BitStack FillContent Gap="2rem"> + <BitStack FillContent> + <BitPivot Alignment="BitAlignment.Center"> + @* <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.UserName)]"> + <BitTextField @bind-Value="signUpModel.UserName" + AutoFocus TabIndex="1" + Type="BitInputType.Text" + Label="@Localizer[nameof(AppStrings.UserName)]" + Placeholder="@Localizer[nameof(AppStrings.UserName)]" /> + <ValidationMessage For="@(() => signUpModel.UserName)" /> + </BitPivotItem> *@ + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]"> + <BitTextField @bind-Value="signUpModel.Email" + AutoFocus TabIndex="1" + Type="BitInputType.Email" + Label="@Localizer[nameof(AppStrings.Email)]" + Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> + <ValidationMessage For="@(() => signUpModel.Email)" /> + </BitPivotItem> + + <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.PhoneNumber)]"> + <BitTextField @bind-Value="signUpModel.PhoneNumber" + AutoFocus TabIndex="1" + Type="BitInputType.Tel" + Label="@Localizer[nameof(AppStrings.PhoneNumber)]" + Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> + <ValidationMessage For="@(() => signUpModel.PhoneNumber)" /> + </BitPivotItem> + </BitPivot> + + <BitTextField @bind-Value="signUpModel.Password" CanRevealPassword + TabIndex="2" + Type="BitInputType.Password" + Label="@Localizer[nameof(AppStrings.Password)]" + AutoComplete="@BitAutoCompleteValue.NewPassword" + Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]" /> + <ValidationMessage For="@(() => signUpModel.Password)" /> + </BitStack> + + @*#if (captcha == "reCaptcha")*@ + <GoogleRecaptcha /> + @*#endif*@ + + <BitButton IsLoading="isWaiting" ButtonType="BitButtonType.Submit"> + @Localizer[nameof(AppStrings.SignUp)] + </BitButton> + </BitStack> + </EditForm> + <br /> + <BitText Typography="BitTypography.Body2"> + @Localizer[nameof(AppStrings.SignInMessageInSignUp)] + <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> + @Localizer[nameof(AppStrings.Or)] + <BitLink Href="@($"{Urls.ConfirmPage}?email={Uri.EscapeDataString(signUpModel.Email??"")}&phoneNumber={Uri.EscapeDataString(signUpModel.PhoneNumber??"")}")"> + @Localizer[nameof(AppStrings.Confirm)] + </BitLink> + </BitText> + <BitText Typography="BitTypography.Body2"> + By signing up, you agree to our <BitLink Href="@Urls.TermsPage">@Localizer[nameof(AppStrings.Terms)]</BitLink> + </BitText> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs similarity index 66% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs index ad30fb7139..9d356140e4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs @@ -2,13 +2,11 @@ using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Shared.Controllers.Identity; -namespace Boilerplate.Client.Core.Components.Pages.Identity; +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignUp; public partial class SignUpPage { private bool isWaiting; - private string? message; - private ElementReference messageRef = default!; private readonly SignUpRequestDto signUpModel = new() { UserName = Guid.NewGuid().ToString() }; @@ -16,6 +14,7 @@ public partial class SignUpPage [AutoInject] private IIdentityController identityController = default!; [AutoInject] private IExternalNavigationService externalNavigationService = default!; + private async Task DoSignUp() { if (isWaiting) return; @@ -24,8 +23,7 @@ private async Task DoSignUp() var googleRecaptchaResponse = await JSRuntime.GoogleRecaptchaGetResponse(); if (string.IsNullOrWhiteSpace(googleRecaptchaResponse)) { - message = Localizer[nameof(AppStrings.InvalidGoogleRecaptchaChallenge)]; - await messageRef.ScrollIntoView(); + SnackBarService.Error(Localizer[nameof(AppStrings.InvalidGoogleRecaptchaChallenge)]); return; } @@ -33,8 +31,6 @@ private async Task DoSignUp() //#endif isWaiting = true; - message = null; - StateHasChanged(); try { @@ -54,10 +50,11 @@ private async Task DoSignUp() } catch (KnownException e) { - message = e is ResourceValidationException re - ? string.Join(" ", re.Payload.Details.SelectMany(d => d.Errors).Select(e => e.Message)) - : e.Message; - await messageRef.ScrollIntoView(); + var message = e is ResourceValidationException re + ? string.Join(" ", re.Payload.Details.SelectMany(d => d.Errors).Select(e => e.Message)) + : e.Message; + + SnackBarService.Error(message); //#if (captcha == "reCaptcha") await JSRuntime.GoogleRecaptchaReset(); @@ -69,28 +66,8 @@ private async Task DoSignUp() } } - private async Task GoogleSignUp() - { - await SocialSignUp("Google"); - } - - private async Task GitHubSignUp() - { - await SocialSignUp("GitHub"); - } - - private async Task TwitterSignUp() - { - await SocialSignUp("Twitter"); - } - private async Task SocialSignUp(string provider) { - if (isWaiting) return; - - isWaiting = true; - message = null; - try { var port = localHttpServer.Start(CurrentCancellationToken); @@ -101,17 +78,7 @@ private async Task SocialSignUp(string provider) } catch (KnownException e) { - isWaiting = false; - - message = e.Message; - - await messageRef.ScrollIntoView(); - } - catch - { - isWaiting = false; - - throw; + SnackBarService.Error(e.Message); } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.scss new file mode 100644 index 0000000000..50d0d089dd --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.scss @@ -0,0 +1,24 @@ +@import '../../../../Styles/abstracts/_media-queries.scss'; + +section { + width: 100%; + height: 100%; +} + +::deep { + form { + width: 304px; + } + + .lg-sep { + @include lt-md { + display: none; + } + } + + .sm-sep { + @include gt-sm { + display: none; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor deleted file mode 100644 index 9f47c1da2c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor +++ /dev/null @@ -1,126 +0,0 @@ -@*+:cnd:noEmit*@ -@attribute [Route(Urls.SignUpPage)] -@attribute [Route("{culture?}" + Urls.SignUpPage)] -@inherits AppComponentBase -@using Boilerplate.Client.Core.Components.Pages.Identity.Components - -<PageTitle>@Localizer[nameof(AppStrings.SingUpTitle)]</PageTitle> - -<div class="page-container"> - <div class="form"> - <div @ref="messageRef" class="form-message-bar"> - @if (string.IsNullOrEmpty(message) is false) - { - <BitMessage Multiline Color="BitColor.Error" OnDismiss="() => message = null"> - @message - </BitMessage> - } - </div> - - <BitText Typography="BitTypography.H4" Gutter> - @Localizer[nameof(AppStrings.SignUp)] - </BitText> - <br /><br /> - <BitStack Horizontal> - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(GoogleSignUp)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.GoogleSignUpButtonText]"> - <GoogleIcon /> - </BitButton> - - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(GitHubSignUp)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.GitHubSignUpButtonText]"> - <GitHubIcon /> - </BitButton> - - <BitButton IsLoading="isWaiting" - Size="BitSize.Small" - Variant="BitVariant.Text" - ButtonType="BitButtonType.Button" - OnClick="WrapHandled(TwitterSignUp)" - Color="BitColor.SecondaryBackground" - Title="@Localizer[AppStrings.TwitterSignUpButtonText]"> - <TwitterIcon /> - </BitButton> - </BitStack> - <br /> - <BitSeparator Style="width:100%">@Localizer[AppStrings.Or]</BitSeparator> - <br /> - <EditForm Model="signUpModel" OnValidSubmit="WrapHandled(DoSignUp)"> - <AppDataAnnotationsValidator /> - - <BitPivot> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.Email)]"> - <br /> - <div class="form-input-container"> - <BitTextField @bind-Value="signUpModel.Email" - Type="BitInputType.Email" - Label="@Localizer[nameof(AppStrings.Email)]" - Placeholder="@Localizer[nameof(AppStrings.EmailPlaceholder)]" /> - <ValidationMessage For="@(() => signUpModel.Email)" /> - </div> - </BitPivotItem> - <BitPivotItem HeaderText="@Localizer[nameof(AppStrings.PhoneNumber)]"> - <br /> - <div class="form-input-container"> - <BitTextField @bind-Value="signUpModel.PhoneNumber" - Type="BitInputType.Tel" - Label="@Localizer[nameof(AppStrings.PhoneNumber)]" - Placeholder="@Localizer[nameof(AppStrings.PhoneNumberPlaceholder)]" /> - <ValidationMessage For="@(() => signUpModel.PhoneNumber)" /> - </div> - </BitPivotItem> - </BitPivot> - <div class="form-input-container"> - <BitTextField @bind-Value="signUpModel.Password" - CanRevealPassword="true" - Type="BitInputType.Password" - Label="@Localizer[nameof(AppStrings.Password)]" - AutoComplete="@BitAutoCompleteValue.NewPassword" - Placeholder="@Localizer[nameof(AppStrings.PasswordPlaceholder)]" /> - <ValidationMessage For="@(() => signUpModel.Password)" /> - </div> - @* <div class="form-input-container"> - <BitCheckbox Class="form-checkbox" @bind-Value="signUpModel.TermsAccepted"> - <LabelTemplate> - <span>@Localizer[nameof(AppStrings.TermsMessage)] <BitLink Href="@Urls.TermsPage">@Localizer[nameof(AppStrings.TermsTitle)]</BitLink></span> - </LabelTemplate> - </BitCheckbox> - <ValidationMessage For="@(() => signUpModel.TermsAccepted)" /> - </div> *@ - - @*#if (captcha == "reCaptcha")*@ - <div class="form-input-container"> - <GoogleRecaptcha /> - </div> - @*#endif*@ - - <BitButton IsLoading="isWaiting" - Class="form-submit-button" - ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.SignUp)] - </BitButton> - <br /><br /> - <BitText Typography="BitTypography.Body2" Gutter> - @Localizer[nameof(AppStrings.SignInMessageInSignUp)] - <BitLink Href="@Urls.SignInPage">@Localizer[nameof(AppStrings.SignIn)]</BitLink> - @Localizer[nameof(AppStrings.Or)] - <BitLink Href="@($"{Urls.ConfirmPage}?email={Uri.EscapeDataString(signUpModel.Email??"")}&phoneNumber={Uri.EscapeDataString(signUpModel.PhoneNumber??"")}")"> - @Localizer[nameof(AppStrings.Confirm)] - </BitLink> - </BitText> - <BitText Typography="BitTypography.Body2"> - By signing up, you agree to our <BitLink Href="@Urls.TermsPage">@Localizer[nameof(AppStrings.TermsTitle)]</BitLink> - </BitText> - </EditForm> - </div> -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.scss deleted file mode 100644 index f68317def7..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUpPage.razor.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; - -.page-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/_Imports.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/_Imports.razor new file mode 100644 index 0000000000..0d11f0a626 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/_Imports.razor @@ -0,0 +1 @@ +@using Boilerplate.Client.Core.Components.Pages.Identity.Components \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor index a0e7507dfc..2f0047c280 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor @@ -1,20 +1,21 @@ @attribute [Route(Urls.NotAuthorizedPage)] @attribute [Route("{culture?}" + Urls.NotAuthorizedPage)] -@inherits AppComponentBase +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.NotAuthorizedPageTitle)]</PageTitle> <AuthorizeView> - <div class="main"> - <div><img src="_content/Boilerplate.Client.Core/images/icons/error-triangle.svg" /></div> - <h2 class="title"> - @Localizer[nameof(AppStrings.ForbiddenException)] - </h2> - <h4 class="description"> - @Localizer[nameof(AppStrings.YouAreSignInAs)] @user.GetDisplayName() - </h4> - <div class="buttons"> - <BitButton OnClick="WrapHandled(SignIn)"> - @Localizer[nameof(AppStrings.SignInAsDifferentUser)] - </BitButton> - </div> - </div> + <section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitImage Src="_content/Boilerplate.Client.Core/images/403.svg" Width="100%" Style="max-width:600px" /> + + <BitText Typography="BitTypography.H5" Color="BitColor.SevereWarning" Align="BitTextAlign.Center"> + @Localizer[nameof(AppStrings.ForbiddenException)] + </BitText> + + <BitText>@Localizer[nameof(AppStrings.YouAreSignInAs)] <b>@user.GetDisplayName()</b></BitText> + + <BitButton OnClick="WrapHandled(SignIn)">@Localizer[nameof(AppStrings.SignInAsDifferentUser)]</BitButton> + </BitStack> + </section> </AuthorizeView> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.cs index 650adcfff3..9d76ecf3ec 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.cs @@ -1,4 +1,6 @@ -namespace Boilerplate.Client.Core.Components.Pages; +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Components.Pages; public partial class NotAuthorizedPage { @@ -6,6 +8,8 @@ public partial class NotAuthorizedPage [SupplyParameterFromQuery(Name = "return-url"), Parameter] public string? ReturnUrl { get; set; } + [AutoInject] private ILogger<NotAuthorizedPage> logger = default!; + protected override async Task OnParamsSetAsync() { user = (await AuthenticationStateTask).User; @@ -19,12 +23,14 @@ protected override async Task OnAfterFirstRenderAsync() // Let's update the access token by refreshing it when a refresh token is available. // Following this procedure, the newly acquired access token may now include the necessary roles or claims. - // To prevent infinitie redirect loop, let's append refresh_token=false to the url, so we only redirect in case no refresh_token=false is present + // To prevent infinitie redirect loop, let's append try_refreshing_token=false to the url, so we only redirect in case no try_refreshing_token=false is present if (string.IsNullOrEmpty(refresh_token) is false && ReturnUrl?.Contains("try_refreshing_token=false", StringComparison.InvariantCulture) is null or false) { await AuthenticationManager.RefreshToken(); + logger.LogInformation("Refreshing access token."); + if ((await AuthenticationStateTask).User.IsAuthenticated()) { if (ReturnUrl is not null) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.scss index 96fc025b37..a5b3aaf4d0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotAuthorizedPage.razor.scss @@ -1,28 +1,5 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_bit-css-variables.scss'; - -.main { +section { width: 100%; - display: flex; - padding: rem2(8px); - text-align: center; - max-width: rem2(800px); - flex-direction: column; - max-height: rem2(600px); - border-radius: rem2(4px); - background-color: $bit-color-background-primary; -} - -.title { - color: $bit-color-error; -} - -.description { - overflow: auto; - white-space: pre; - margin: rem2(24px); -} - -.buttons { - margin-bottom: rem2(24px); -} + height: 100%; + padding: 1rem; +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor index 900e143153..bef3f5248e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor @@ -1,10 +1,13 @@ @attribute [Route(Urls.NotFoundPage)] @attribute [Route("{culture?}" + Urls.NotFoundPage)] -@inherits AppComponentBase +@inherits AppPageBase -<div class="main"> - <h1 class="title">404</h1> - <div class="description"> - @Localizer[nameof(AppStrings.NotFoundText)] - </div> -</div> \ No newline at end of file +<PageTitle>@Localizer[nameof(AppStrings.NotFoundPageTitle)]</PageTitle> + +<section> + <BitStack HorizontalAlign="BitAlignment.Center"> + <BitImage Src="_content/Boilerplate.Client.Core/images/404.svg" Width="100%" Style="max-width:600px" /> + <BitText Typography="BitTypography.H4">@Localizer[nameof(AppStrings.NotFoundText)]</BitText> + <BitButton Href="@Urls.HomePage">@Localizer[nameof(AppStrings.BackToHome)]</BitButton> + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor.scss index 52bf68d2a7..1511b6f6de 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/NotFoundPage.razor.scss @@ -1,30 +1,4 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; - -.main { - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} - -.title { - font-weight: 600; - font-size: rem2(120px); - line-height: rem2(168px); - - @include lt-xl { - font-size: rem2(100px); - } -} - -.description { - font-weight: 400; - font-size: rem2(18px); - line-height: rem2(28px); - - @include lt-xl { - font-size: rem2(16px); - line-height: rem2(24px); - } -} +section { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor deleted file mode 100644 index af94062b2b..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor +++ /dev/null @@ -1,68 +0,0 @@ -@attribute [Route(Urls.OfflineEditProfilePage)] -@attribute [Route("{culture?}" + Urls.OfflineEditProfilePage)] -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.EditProfileTitle)]</PageTitle> - -<div class="page-container"> - <div class="content-container profile-panel"> - @if (string.IsNullOrEmpty(editProfileMessage) is false) - { - <BitMessage Class="form-message-bar" Color="editProfileMessageColor" OnDismiss="() => editProfileMessage = null"> - @editProfileMessage - </BitMessage> - } - - <BitText Typography="BitTypography.H2" Gutter> - @Localizer[nameof(AppStrings.EditProfileTitle)] - </BitText> - - @if (isLoading) - { - <div class="loading-container"> - <BitRingLoading /> - </div> - } - else - { - <EditForm Model="userToEdit" OnValidSubmit="WrapHandled(DoSave)" class="edit-profile-form"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="userToEdit.FullName" - Label="@Localizer[nameof(AppStrings.FullName)]" - Placeholder="@Localizer[nameof(AppStrings.FullName)]" /> - <ValidationMessage For="@(() => userToEdit.FullName)" /> - </div> - - <div class="form-input-container"> - <BitDatePicker IsResponsive @bind-Value="userToEdit.BirthDate" - Class="edit-profile-dtp" - Label="@Localizer[nameof(AppStrings.BirthDate)]" - GoToTodayTitle="@Localizer[nameof(AppStrings.GoToToday)]" - Placeholder="@Localizer[nameof(AppStrings.SelectBirthDate)]" /> - <ValidationMessage For="@(() => userToEdit.BirthDate)" /> - </div> - - <div class="form-choice-container"> - <BitChoiceGroup Horizontal - @bind-Value="userToEdit.GenderAsString" - TItem="BitChoiceGroupOption<string>" TValue="string" - Label="@Localizer[nameof(AppStrings.Gender)]"> - <BitChoiceGroupOption Value="@Gender.Male.ToString()" - Text="@Localizer[nameof(AppStrings.GenderMale)]" /> - <BitChoiceGroupOption Value="@Gender.Female.ToString()" - Text="@Localizer[nameof(AppStrings.GenderFemale)]" /> - <BitChoiceGroupOption Value="@Gender.Other.ToString()" - Text="@Localizer[nameof(AppStrings.GenderOther)]" /> - </BitChoiceGroup> - </div> - - <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit" Title="@Localizer[nameof(AppStrings.Save)]"> - @Localizer[nameof(AppStrings.Save)] - </BitButton> - </EditForm> - - } - </div> -</div> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.scss deleted file mode 100644 index a6ecf430a3..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Offline/OfflineEditProfilePage.razor.scss +++ /dev/null @@ -1,103 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; - padding: rem2(48px) rem2(16px); - - @include lt-lg { - padding: rem2(16px); - } -} - -.content-container { - width: 100%; - display: flex; - position: relative; - align-items: center; - max-width: rem2(608px); - border-radius: rem2(4px); - flex-flow: column nowrap; - padding: rem2(32px) rem2(14px); - background-color: $bit-color-background-primary; - - &.profile-panel { - box-shadow: $bit-box-shadow-callout; - } - - &.danger-panel { - border: 2px solid darkred; - } -} - -.form-message-bar { - position: absolute; - inset-block-start: 0; - inset-inline-start: 0; - border-radius: rem2(4px) rem2(4px) 0 0; -} - -.page-title { - font-weight: 600; - font-size: rem2(28px); - line-height: rem2(44px); - margin-bottom: rem2(16px); -} - -.form-profile-container { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - max-width: rem2(340px); - margin-bottom: rem2(16px); - justify-content: flex-start; -} - -.form-input-container { - width: 100%; - display: flex; - max-width: rem2(340px); - flex-flow: column nowrap; - margin-bottom: rem2(17px); -} - -.form-input-error { - font-size: rem2(12px); - line-height: rem2(16px); - color: $bit-color-error; -} - -.form-choice-container { - width: 100%; - max-width: rem2(340px); - margin-bottom: rem2(16px); -} - -::deep .edit-profile-dtp { - .bit-dtp-wrapper { - z-index: 5; - } - - .bit-dtp-overlay { - z-index: 4; - } - - .bit-dtp-callout { - z-index: 6; - } -} - -::deep .edit-profile-form { - width: 100%; - display: flex; - align-items: center; - flex-flow: column nowrap; - justify-content: flex-start; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor deleted file mode 100644 index 9ea27af533..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor +++ /dev/null @@ -1,61 +0,0 @@ -@inherits AppComponentBase - -<BitModal @bind-IsOpen="isOpen" IsBlocking="true" AutoToggleScroll="false"> - <div class="main-container"> - <div class="header"> - <span class="title"> - @if (product.Id != default) - { - @Localizer[nameof(AppStrings.EditProduct)] - } - else - { - @Localizer[nameof(AppStrings.AddProduct)] - } - </span> - <BitButton IconName="@BitIconName.ChromeClose" OnClick="OnCloseClick" Variant="BitVariant.Text" /> - </div> - <EditForm Model="product" OnValidSubmit="WrapHandled(Save)" style="width: 100%; display: flex; flex-direction: column;align-items: center"> - <AppDataAnnotationsValidator /> - - <div class="form-input-container"> - <BitTextField @bind-Value="product.Name" - AutoComplete="@BitAutoCompleteValue.Off" - Label="@Localizer[(nameof(AppStrings.Name))]" - Placeholder="@Localizer[nameof(AppStrings.EnterProductName)]" /> - <ValidationMessage For="() => product.Name" /> - </div> - - <div class="form-input-container"> - <BitDropdown @bind-Value="selectedCategoyId" - IsMultiSelect="false" - Items="allCategoryList" - OnSelectItem="((BitDropdownItem<string> item) => { product.CategoryId = Guid.Parse(item.Value!); product.CategoryName = item.Text; })" - Placeholder="@Localizer[(nameof(AppStrings.SelectCategory))]" - Label="@Localizer[(nameof(AppStrings.SelectCategory))]" /> - <ValidationMessage For="@(() => product.CategoryId)" /> - </div> - - <div class="form-input-container"> - <BitNumberField @bind-Value="product.Price" - Precision="2" - Suffix="$" - NumberFormat="C2" - Label="@Localizer[(nameof(AppStrings.Price))]" /> - <ValidationMessage For="() => product.Price" /> - </div> - - <div class="form-input-container"> - <BitTextField @bind-Value="product.Description" - Rows="3" - IsMultiline="true" - Label="@Localizer[(nameof(AppStrings.Description))]" /> - <ValidationMessage For="() => product.Description" /> - </div> - - <BitButton IsLoading="isSaving" ButtonType="BitButtonType.Submit"> - @Localizer[nameof(AppStrings.Save)] - </BitButton> - </EditForm> - </div> -</BitModal> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.scss deleted file mode 100644 index 9a0cfceb0b..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/AddOrEditProductModal.razor.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.main-container { - display: flex; - width: rem2(560px); - align-items: center; - flex-direction: column; - min-height: rem2(378px); - border-radius: rem2(2px); - justify-content: flex-start; - padding: rem2(16px) rem2(24px); - background-color: $bit-color-background-primary; - - .bit-ios & { - padding: rem2(16px) rem2(24px) calc(rem2(16px) + env(safe-area-inset-bottom)); - } - - @include sm { - width: 100%; - position: fixed; - inset-block-end: 0; - inset-inline-start: 0; - border-radius: rem2(8px) rem2(8px) 0 0; - } -} - -.header { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - margin-bottom: rem2(24px); - justify-content: space-between; -} - -.title { - font-weight: 600; - font-size: rem2(20px); - line-height: rem2(28px); -} - -.form-input-container { - width: 100%; - margin-bottom: rem2(24px); -} - -::deep .validation-message { - color: $bit-color-error; -} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor deleted file mode 100644 index 381652b494..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor +++ /dev/null @@ -1,69 +0,0 @@ -@attribute [Route(Urls.ProductsPage)] -@attribute [Route("{culture?}" + Urls.ProductsPage)] -@using Boilerplate.Shared.Dtos.Products -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.ProductsPageTitle)]</PageTitle> - -<div class="page-container"> - <div class="page-row"> - <h1 class="page-title">@Localizer[nameof(AppStrings.Products)]</h1> - <BitButton OnClick="WrapHandled(CreateProduct)">@Localizer[nameof(AppStrings.AddProduct)]</BitButton> - </div> - - <div class="grid"> - - <div class="grid-container"> - <BitDataGrid @ref="dataGrid" ItemsProvider="productsProvider" TGridItem="ProductDto" ResizableColumns="true" Pagination="pagination"> - <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Name)]" Property="p => p!.Name" Sortable="true" Class="column1"> - <ColumnOptions> - <BitStack Horizontal> - <BitSearchBox @bind-Value="ProductNameFilter" - Immediate DebounceTime="500" - Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" - InputHtmlAttributes="@(new Dictionary<string, object> {{"autofocus", true}})" /> - @if (isLoading) - { - <div class="loading-container"> - <BitEllipsisLoading CustomSize="32" /> - </div> - } - </BitStack> - </ColumnOptions> - </BitDataGridPropertyColumn> - <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Category)]" Property="p => p!.CategoryName" Sortable="true" Align="BitDataGridAlign.Left"> - <ColumnOptions> - <BitStack Horizontal> - <BitSearchBox @bind-Value="CategoryNameFilter" - Immediate DebounceTime="500" - Placeholder="@Localizer[(nameof(AppStrings.SearchOnName))]" - InputHtmlAttributes="@(new Dictionary<string, object> {{"autofocus", true}})" /> - @if (isLoading) - { - <div class="loading-container"> - <BitEllipsisLoading CustomSize="32" /> - </div> - } - </BitStack> - </ColumnOptions> - </BitDataGridPropertyColumn> - <BitDataGridPropertyColumn Title="@Localizer[nameof(AppStrings.Price)]" Property="p => p!.Price" Sortable="true" Align="BitDataGridAlign.Left" Format="C2" /> - <BitDataGridTemplateColumn Title="@Localizer[(nameof(AppStrings.Action))]" Align="BitDataGridAlign.Center" Context="product"> - <BitButton IconName="@BitIconName.Edit" - Variant="BitVariant.Text" - OnClick="() => EditProduct(product!)" - Title="@Localizer[(nameof(AppStrings.Edit))]" /> - <BitButton Color="BitColor.Error" - Variant="BitVariant.Text" - IconName="@BitIconName.Delete" - OnClick="WrapHandled(() => DeleteProduct(product!))" - Title="@Localizer[(nameof(AppStrings.Delete))]" /> - </BitDataGridTemplateColumn> - </BitDataGrid> - </div> - <BitDataGridPaginator Value="pagination" /> - </div> -</div> - -<AddOrEditProductModal @ref="modal" OnSave="WrapHandled(RefreshData)" /> -<ConfirmMessageBox @ref=confirmMessageBox /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.scss deleted file mode 100644 index 33c0477e72..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.scss +++ /dev/null @@ -1,131 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - flex-grow: 1; - display: flex; - align-items: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.page-row { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - margin-bottom: rem2(24px); - justify-content: space-between; -} - -.page-title { - margin: 0; - font-weight: 600; - font-size: rem2(20px); - line-height: rem2(28px); - - @include sm { - font-size: rem2(18px); - line-height: rem2(24px); - } -} - -.grid-container { - overflow: auto; -} - -.grid { - width: 100%; - display: inline-flex; - flex-direction: column; - border-radius: rem2(2px); - border: rem2(1px) solid $bit-color-border-secondary; - - ::deep { - .id-col { - width: 90px; - } - - .bitdatagrid th .col-options-button { - cursor: help; - } - } -} - -::deep table { - min-width: 100%; - border-spacing: 0; -} - -::deep thead { - height: rem2(43px); - background-color: $bit-color-background-primary; -} - -::deep tr { - height: rem2(43px); -} - -::deep th { - font-weight: 600; - padding: 0 rem2(12px); - font-size: rem2(14px); - line-height: rem2(20px); -} - -::deep td { - font-weight: 400; - white-space: nowrap; - font-size: rem2(12px); - line-height: rem2(16px); - border-top: rem2(1px) solid $bit-color-border-secondary; -} - -::deep tr td:first-child { - font-size: rem2(14px); - line-height: rem2(20px); - padding-inline-start: rem2(12px); -} - -::deep .col-options-button { - &:hover { - background-color: transparent; - } -} - -::deep .col-title { - &:hover { - background-color: transparent !important; - } -} - -::deep .product-search-box { - width: 100%; - max-width: rem2(300px); - margin-bottom: rem2(24px); -} - -::deep th:nth-child(2) .col-options-button { - cursor: pointer; - background-image: none; - - &:before { - content: "\E721"; - font-style: normal; - font-weight: normal; - display: inline-block; - font-family: 'Fabric MDL2'; - } -} - -::deep .bitdatagrid-paginator { - margin-top: 0; - overflow: auto; - padding: 0 rem2(12px); - font-size: rem2(14px); - min-height: rem2(43px); - border-top: rem2(1px) solid $bit-color-border-secondary; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor index b31c8dd864..cbb91172e5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor @@ -1,107 +1,117 @@ @attribute [Route(Urls.TermsPage)] @attribute [Route("{culture?}" + Urls.TermsPage)] -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.TermsTitle)]</PageTitle> - -<div class="page-container"> - <div class="page-content"> - - <section class="main-section"> - <h1 class="main-section-title">Boilerplate license</h1> - <div class="section-desc"> - This EULA and Privacy Policy are written for individuals who use the Boilerplate Demo Version through the <BitLink Href="https://todo.bitplatform.dev" Target="_blank">todo.bitplatform.dev</BitLink> website, or the published version of this app on the bit platform's Google Play, Apple Store, and Microsoft Store accounts. - <br /> - If you want to build your own project based on this Project Template, please refer to the <BitLink Href="https://github.com/bitfoundation/bitplatform/blob/develop/LICENSE" Target="_blank">License</BitLink> and create your own project, publish it with the relevant EULA and Privacy Policy for your business.s - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">End-User License Agreement (EULA) for Boilerplate Demo Version</h2> - <div class="section-desc"> - This End-User License Agreement (EULA) is a legal agreement between you (the "user") and bit platform, located at Sixmastraat 15, 8932 PA Leeuwarden, Netherlands, with phone number <BitLink Href="tel:+31684207362">+31684207362</BitLink>, for the use of Boilerplate Demo Version (the "Software"). - <br /> - By using the Software, you agree to be bound by the terms and conditions of this agreement. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">License Grant</h2> - <div class="section-desc"> - bit platform hereby grants the user a non-exclusive, non-transferable license to use the Software, solely for the purpose of evaluating and testing the features of the Boilerplate Demo Version during the term of this EULA. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Ownership</h2> - <div class="section-desc"> - bit platform retains all rights, title, and interest in and to the Software, including any and all intellectual property rights. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">User Data</h2> - <div class="section-desc"> - bit platform may collect user data, including name, surname, photo, date of birth, and gender. Providing this information is optional, and bit platform will only use it for the purposes of the Boilerplate Demo Version. If the user requests, bit platform will delete this data within one day, using one of the contact methods provided at the end of this EULA. In addition, the user is required to provide an email address for account verification purposes, but bit platform will not use this email for any other purposes. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Analytics and Cookies</h2> - <div class="section-desc"> - bit platform does not use any Analytics tools for measuring website traffic and only uses strictly necessary cookies. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Restrictions</h2> - <div class="section-desc"> - User may not: (a) copy the Software, except as necessary for testing purposes; (b) distribute, rent, loan, lease, sell, sublicense, or otherwise transfer the Software or any portion thereof; (c) reverse engineer, decompile, disassemble, modify, translate, make any attempt to discover the source code of the Software, or create derivative works based on the Software; or (d) remove any proprietary notices or labels on the Software. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">No Warranty</h2> - <div class="section-desc"> - The Software is provided "AS IS" and without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. bit platform does not warrant that the Software will be error-free or that its use will be uninterrupted. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Limitation of Liability</h2> - <div class="section-desc"> - bit platform shall not be liable for any damages whatsoever arising out of the use of or inability to use the Software, including but not limited to direct, indirect, incidental, special, or consequential damages, even if bit platform has been advised of the possibility of such damages. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Termination</h2> - <div class="section-desc"> - This EULA shall remain in effect until 2025-12-31. bit platform reserves the right to terminate this EULA at any time without notice. Upon termination, the user shall immediately cease all use of the Software and destroy all copies of the Software in its possession. - </div> - </section> - - <section class="sub-section"> - <h2 class="sub-section-title">Contact Information</h2> - <div class="section-desc"> - If you have any questions or concerns regarding this EULA or the Privacy Policy, or if you would like to contact bit platform for any reason, please email <BitLink Href="mailto:info@bitplatform.dev">info@bitplatform.dev</BitLink> or call <BitLink Href="tel:+31684207362">+31684207362</BitLink>, or visit our website at <BitLink Href="https://bitplatform.dev/contact-us" Target="_blank">our website</BitLink> or visit us at Sixmastraat 15, 8932 PA Leeuwarden, Netherlands. - </div> - </section> - - - <section class="main-section"> - <h1 class="main-section-title">Privacy Policy</h1> - <div class="section-desc"> - <ul> - <li>bit platform is committed to protecting the privacy of the user data.</li> - <li>We will only use the data collected from the user for the purposes of the Boilerplate Demo Version and will delete this data upon user request, using one of the contact methods provided above.</li> - <li>We do not share the user data with any third parties and do not allow users to share data with anyone in any way.</li> - <li>We do not collect any information on user's usage in the Boilerplate Demo Version.</li> - <li>We do not display any Third-Party content, advertisements, or any purchases in the Boilerplate Demo Version.</li> - <li>This Privacy Policy is effective until 2025-12-31 and is subject to change without notice.</li> - </ul> - </div> - </section> - - </div> -</div> +@inherits AppPageBase + +<PageTitle>@Localizer[nameof(AppStrings.TermsPageTitle)]</PageTitle> + +<article> + <BitStack> + <BitText Typography="BitTypography.H3">Boilerplate license</BitText> + <BitText> + This EULA and Privacy Policy are written for individuals who use the Boilerplate Demo Version through the + <BitLink Href="https://use-your-server-url-here.com" Target="_blank">https://use-your-server-url-here.com</BitLink> website, or the + published version of this app on the bit platform's Google Play, Apple Store, and Microsoft Store accounts. + <br /> + If you want to build your own project based on this Project Template, please refer to the + <BitLink Href="https://github.com/bitfoundation/bitplatform/blob/develop/LICENSE" Target="_blank">License</BitLink> + and create your own project, publish it with the relevant EULA and Privacy Policy for your business. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">End-User License Agreement (EULA) for Boilerplate Demo Version</BitText> + <BitText> + This End-User License Agreement (EULA) is a legal agreement between you (the "user") and bit platform, located at + Sixmastraat 15, 8932 PA Leeuwarden, Netherlands, with phone number <BitLink Href="tel:+31684207362">+31684207362</BitLink>, + for the use of Boilerplate Demo Version (the "Software"). + <br /> + By using the Software, you agree to be bound by the terms and conditions of this agreement. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">License Grant</BitText> + <BitText> + bit platform hereby grants the user a non-exclusive, non-transferable license to use the Software, solely for + the purpose of evaluating and testing the features of the Boilerplate Demo Version during the term of this EULA. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Ownership</BitText> + <BitText> + bit platform retains all rights, title, and interest in and to the Software, including any and all intellectual property rights. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">User Data</BitText> + <BitText> + bit platform may collect user data, including name, surname, photo, date of birth, gender, IP address, device operating system name and its version. + Providing certain information is optional, and bit platform will only use it for the purposes of the Boilerplate Demo Version. + If the user requests, bit platform will delete this data within one day, using one of the contact methods provided at the end of this EULA. + In addition, the user is required to provide an email address for account verification purposes, but bit platform will not use this email for any other purposes. + For more information on data collection and usage by AppInsights and AppCenter, please refer to their documents: + <br /> + <BitLink Href="https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview">Application Insights overview</BitLink> + <br /> + <BitLink Href="https://learn.microsoft.com/en-us/appcenter/">Visual Studio App Center documentation</BitLink> + <br /> + bit platform also provides a feature in the settings page for users to delete their accounts and their data completely + (<BitLink Href="@($"{Urls.SettingsPage}/{Urls.SettingsSections.Account}")">Account settings</BitLink>). + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Push Notification</BitText> + <BitText> + The Software includes push notifications for updates and alerts. to enable this feature the app uses the device id + generated by the native features available in the operating system. Users can opt-out of these notifications through the Software’s settings. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Analytics and Cookies</BitText> + <BitText> + bit platform uses AppInsights and AppCenter to collect data on app performance and user interactions. these data consists of the + User id (generated by the app), User-Session id (an id generated by the app), App-Session id (an id generated by the app), + Operating System name and version, and App version. bit platform will not use these data for any purposes other than analytics + and in-app features (such as showing all active sessions of the user in the settings page). + The Software also uses necessary cookies for authentication/authorization to ensure proper functionality of the app. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Restrictions</BitText> + <BitText> + User may not: (a) copy the Software, except as necessary for testing purposes; (b) distribute, rent, loan, lease, sell, sublicense, + or otherwise transfer the Software or any portion thereof; (c) reverse engineer, decompile, disassemble, modify, translate, make any + attempt to discover the source code of the Software, or create derivative works based on the Software; or (d) remove any proprietary + notices or labels on the Software. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">No Warranty</BitText> + <BitText> + The Software is provided "AS IS" and without warranty of any kind, express or implied, including but not limited to the warranties + of merchantability, fitness for a particular purpose, and non-infringement. bit platform does not warrant that the Software will be + error-free or that its use will be uninterrupted. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Limitation of Liability</BitText> + <BitText> + bit platform shall not be liable for any damages whatsoever arising out of the use of or inability to use the Software, including + but not limited to direct, indirect, incidental, special, or consequential damages, even if bit platform has been advised of the + possibility of such damages. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Termination</BitText> + <BitText> + This EULA shall remain in effect until 2025-12-31. bit platform reserves the right to terminate this EULA at any time without notice. + Upon termination, the user shall immediately cease all use of the Software and destroy all copies of the Software in its possession. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Contact Information</BitText> + <BitText> + If you have any questions or concerns regarding this EULA or the Privacy Policy, or if you would like to contact bit platform for any reason, + please email <BitLink Href="mailto:info@bitplatform.dev">info@bitplatform.dev</BitLink> + or call <BitLink Href="tel:+31684207362">+31684207362</BitLink>, or visit our website at + <BitLink Href="https://bitplatform.dev/contact-us" Target="_blank">our website</BitLink> or visit us at Sixmastraat 15, 8932 PA Leeuwarden, Netherlands. + </BitText> + <br /> + <BitText Typography="BitTypography.H5">Privacy Policy</BitText> + <ul> + <li><BitText>bit platform is committed to protecting the privacy of the user data.</BitText></li> + <li><BitText>We will only use the data collected from the user for the purposes of the Boilerplate Demo Version and will delete this data upon user request, using one of the contact methods provided above.</BitText></li> + <li><BitText>We do not share the user data with any third parties and do not allow users to share data with anyone in any way.</BitText></li> + <li><BitText>We do not collect any information on user's usage in the Boilerplate Demo Version.</BitText></li> + <li><BitText>We do not display any Third-Party content, advertisements, or any purchases in the Boilerplate Demo Version.</BitText></li> + <li><BitText>This Privacy Policy is effective until 2025-12-31 and is subject to change without notice.</BitText></li> + </ul> + </BitStack> +</article> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.cs index 54ce6ccf48..00f11decd2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.cs @@ -2,4 +2,6 @@ public partial class TermsPage { + protected override string? Title => Localizer[nameof(AppStrings.Terms)]; + protected override string? Subtitle => string.Empty; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.scss index 9dc1d60c45..aa3da598ad 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TermsPage.razor.scss @@ -1,112 +1,2 @@ -@import '../../Styles/abstracts/_functions.scss'; -@import '../../Styles/abstracts/_media-queries.scss'; - -.page-container { - width: 100%; - display: flex; - direction: ltr; - align-items: center; - align-self: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.page-content { - width: 100%; - display: flex; - align-items: center; - max-width: rem2(1128px); - flex-flow: column nowrap; - justify-content: flex-start; - - @include lg { - max-width: rem2(1040px); - } - - @include md { - max-width: rem2(770px); - } -} - -.main-section { - width: 100%; - margin-bottom: rem2(24px); -} - -.main-section-title { - width: 100%; - text-align: start; - font-weight: bold; - font-size: rem2(34px); - line-height: rem2(56px); - margin-bottom: rem2(24px); - - @include lg { - font-size: rem2(30px); - line-height: rem2(48px); - margin-bottom: rem2(16px); - } - - @include md { - font-size: rem2(24px); - line-height: rem2(40px); - margin-bottom: rem2(16px); - } - - @include sm { - font-size: rem2(20px); - line-height: rem2(32px); - margin-bottom: rem2(12px); - } -} - -.section-desc { - width: 100%; - font-size: rem2(18px); - line-height: rem2(28px); - - @include lt-xl { - font-size: rem2(16px); - line-height: rem2(24px); - } - - @include sm { - font-size: rem2(14px); - } -} - -.sub-section { - width: 100%; - margin-bottom: rem2(24px); - - &:last-child { - margin-bottom: 0; - } -} - -.sub-section-title { - width: 100%; - text-align: start; - font-weight: 600; - font-size: rem2(24px); - line-height: rem2(36px); - margin-bottom: rem2(16px); - - @include lg { - font-size: rem2(20px); - line-height: rem2(32px); - margin-bottom: rem2(8px); - } - - @include md { - font-size: rem2(18px); - line-height: rem2(28px); - margin-bottom: rem2(8px); - } - - @include sm { - font-size: rem2(16px); - line-height: rem2(24px); - margin-bottom: rem2(8px); - } +article { } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor deleted file mode 100644 index d69557dbba..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor +++ /dev/null @@ -1,125 +0,0 @@ -@attribute [Route(Urls.TodoPage)] -@attribute [Route("{culture?}" + Urls.TodoPage)] -@inherits AppComponentBase - -<PageTitle>@Localizer[nameof(AppStrings.TodoTitle)]</PageTitle> - -<div class="page-container"> - <div class="search-box-container"> - <BitSearchBox @ref="searchBox" - Style="width: 300px" - OnChange="SearchTodoItems" - Immediate DebounceTime="300" - OnClear="@(() => SearchTodoItems(""))" - Placeholder="@Localizer[nameof(AppStrings.TodoSearchPlaceholder)]" /> - </div> - - <div class="todo-content"> - <h1 class="main-title">@Localizer[nameof(AppStrings.TodoTitle)]</h1> - <div class="add-todo-container"> - <BitTextField @bind-Value="newTodoTitle" - Class="add-todo-input" - Immediate DebounceTime="300" - Placeholder="@Localizer[nameof(AppStrings.TodoAddPlaceholder)]" /> - <BitButton IsLoading="isAdding" - OnClick="WrapHandled(AddTodoItem)" - Title="@Localizer[nameof(AppStrings.Add)]" - IsEnabled="(string.IsNullOrWhiteSpace(newTodoTitle) is false)"> - @Localizer[nameof(AppStrings.Add)] - </BitButton> - </div> - - <div class="todo-list-container"> - <div class="filter-container"> - <BitPivot SelectedKey="@selectedFilter" SelectedKeyChanged="FilterTodoItems"> - <BitPivotItem Key="@nameof(AppStrings.All)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.All)]" /> - <BitPivotItem Key="@nameof(AppStrings.Active)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.Active)]" /> - <BitPivotItem Key="@nameof(AppStrings.Completed)" Class="todo-pivot-tab" HeaderText="@Localizer[nameof(AppStrings.Completed)]" /> - </BitPivot> - <div class="sort-drp-container"> - <BitDropdown Class="sort-todo-drp" - Items="sortItems" - OnSelectItem="(BitDropdownItem<string> item) => SortTodoItems(item)" - DefaultValue="@sortItems[0].Value" - IsResponsive="true"> - <CaretDownTemplate> - <BitIcon IconName="@BitIconName.Sort" Class="sort-todo-icn" /> - </CaretDownTemplate> - </BitDropdown> - </div> - </div> - - <div class="todo-list"> - @if (isLoading) - { - <div class="todo-list-spinner"> - <BitRingLoading /> - </div> - } - else - { - if (viewTodoItems?.Any() is false or null) - { - <div class="todo-list--empty-state"> - <img src="/_content/Boilerplate.Client.Core/images/backgrounds/empty-todo-list-bg.svg"> - <BitLabel>@Localizer[nameof(AppStrings.NoTodos)]</BitLabel> - </div> - } - else - { - <BitBasicList Style="width: 100%; height: inherit" - Items="viewTodoItems" - EnableVirtualization="true"> - <RowTemplate Context="todo"> - <div class="todo-item@(todo.IsInEditMode ? " edit-mode" : "")" role="listitem" @key=@todo.Id> - @if (todo.IsInEditMode) - { - <BitTextField Class="todo-input" @bind-Value="underEditTodoItemTitle" /> - <div class="edit-btn-group"> - <BitButton Title="@Localizer[nameof(AppStrings.Edit)]" OnClick="WrapHandled(() => SaveTodoItem(todo))"> - @Localizer[nameof(AppStrings.Save)] - </BitButton> - <BitButton Variant="BitVariant.Outline" - Class="todo-button" - Title="@Localizer[nameof(AppStrings.Cancel)]" - OnClick="WrapHandled(() => ToggleEditMode(todo))"> - @Localizer[nameof(AppStrings.Cancel)] - </BitButton> - </div> - } - else - { - <div class="todo-info@(todo.IsDone ? " done" : "")"> - <BitCheckbox Label="@todo.Title" - DefaultValue="todo.IsDone" - OnChange="() => ToggleIsDone(todo)" /> - - <div Class="todo-item-date"> - @todo.Date.ToLocalTime().ToString("yyyy MMMM dd, HH:mm:ss") - </div> - </div> - - <div class="todo-btn-group"> - <BitButton IconName="@BitIconName.Edit" - Variant="BitVariant.Text" - Title="@Localizer[nameof(AppStrings.Edit)]" - OnClick="WrapHandled(() => ToggleEditMode(todo))" /> - - <BitButton Color="BitColor.Error" - Variant="BitVariant.Text" - IconName="@BitIconName.Delete" - Title="@Localizer[nameof(AppStrings.Remove)]" - OnClick="WrapHandled(() => DeleteTodoItem(todo))" /> - </div> - } - </div> - </RowTemplate> - </BitBasicList> - } - } - </div> - </div> - </div> -</div> - -<ConfirmMessageBox @ref=confirmMessageBox /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.scss deleted file mode 100644 index 44d610e36b..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Todo/TodoPage.razor.scss +++ /dev/null @@ -1,181 +0,0 @@ -@import '../../../Styles/abstracts/_functions.scss'; -@import '../../../Styles/abstracts/_media-queries.scss'; -@import '../../../Styles/abstracts/_bit-css-variables.scss'; - -.page-container { - width: 100%; - height: 100%; - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} - -.search-box-container { - width: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: rem2(8px) rem2(14px) rem2(24px); - border-bottom: rem2(1px) solid $bit-color-border-secondary; -} - -.todo-content { - width: 100%; - flex-grow: 1; - display: flex; - padding-top: 1rem; - position: relative; - max-width: rem2(608px); - align-items: flex-start; - flex-flow: column nowrap; - justify-content: flex-start; -} - -.main-title { - margin: 0; - font-weight: 600; - font-size: rem2(28px); - line-height: rem2(36px); - margin-bottom: rem2(20px); -} - -.add-todo-container { - width: 100%; - display: flex; - gap: rem2(16px); - align-items: center; - flex-flow: row nowrap; - margin-bottom: rem2(24px); - justify-content: flex-start; -} - -.todo-list-container { - width: 100%; -} - -.filter-container { - width: 100%; - display: flex; - align-items: center; - flex-flow: row nowrap; - justify-content: space-between; -} - -.sort-drp-container { - @media all and (max-width: #{rem2(430px)}) { - position: absolute; - inset-inline-end: 0; - inset-block-start: rem2(25px); - } -} - -.todo-list--empty-state { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-flow: column nowrap; -} - -.todo-list { - width: 100%; - display: flex; - height: rem2(322px); - align-items: center; - margin-top: rem2(4px); - flex-flow: column nowrap; - justify-content: flex-start; - background-color: $bit-color-background-primary; - border: rem2(1px) solid $bit-color-border-secondary; -} - -.todo-item { - width: 100%; - display: flex; - padding: rem2(16px); - align-items: center; - flex-flow: row nowrap; - min-height: rem2(80px); - min-width: fit-content; - justify-content: space-between; - border-bottom: rem2(1px) solid $bit-color-border-secondary; - - &:nth-last-child(-n + 2) { - border-bottom: none; - } - - &.edit-mode { - gap: rem2(16px); - - @media all and (max-width: #{rem2(430px)}) { - flex-flow: column; - - ::deep .todo-input { - width: 100%; - } - } - } -} - -.todo-info { - display: flex; - align-items: flex-start; - justify-content: center; - flex-flow: column nowrap; - - &.done ::deep .bit-chb-txt { - text-decoration: line-through; - } -} - -.todo-item-date { - white-space: nowrap; - font-size: rem2(11px); - line-height: rem2(20px); - margin-block-start: rem2(4px); - margin-inline-start: rem2(28px); - color: $bit-color-foreground-secondary; -} - -.todo-btn-group { - display: flex; - flex-flow: row nowrap; - justify-content: center; -} - -.todo-list-spinner { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -::deep { - .add-todo-input { - flex-grow: 1; - } - - .sort-todo-drp { - width: rem2(136px); - height: rem2(32px); - - .sort-todo-icn { - color: $bit-color-primary; - } - - .bit-drp-iwp .bit-drp-rsp-lbl-ctn { - margin-top: calc(var(--bit-status-bar-height) - rem2(24px)); - - @supports (-webkit-touch-callout: none) { - margin-top: calc(env(safe-area-inset-top) - rem2(24px)); - } - } - } - - .todo-input { - flex-grow: 1; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Parameters.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Parameters.cs new file mode 100644 index 0000000000..8c0f5a830e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Parameters.cs @@ -0,0 +1,23 @@ +namespace Boilerplate.Client.Core.Components; + +public class Parameters +{ + public const string CurrentDir = nameof(CurrentDir); + public const string CurrentUrl = nameof(CurrentUrl); + public const string CurrentTheme = nameof(CurrentTheme); + public const string IsAuthenticated = nameof(IsAuthenticated); + public const string CurrentRouteData = nameof(CurrentRouteData); + + /// <summary> + /// If the current page is part of the cross-layout pages that are rendered in multiple layouts. + /// </summary> + public const string IsCrossLayoutPage = nameof(IsCrossLayoutPage); + + /// <summary> + /// Determines the connection status, with default behavior based on SignalR connection status. + /// If SignalR is not added to the project during initial project creation, this value will always be null by default. + /// Alternatively, you can implement custom logic to control this value. + /// true => Online, false => Offline, null => Unknown + /// </summary> + public const string IsOnline = nameof(IsOnline); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor similarity index 78% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor index 03ef42a6f1..acd9f543b3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor @@ -1,9 +1,10 @@ <CascadingAuthenticationState> - <AppInitializer /> - <LayoutView Layout="@typeof(MainLayout)"> + <ClientAppCoordinator /> + <LayoutView Layout="@typeof(RootLayout)"> <Router AppAssembly="@GetType().Assembly" - AdditionalAssemblies="@(AssemblyLoadContext.Default.Assemblies.Where(asm => asm.GetName().Name?.Contains("Boilerplate") is true))"> + AdditionalAssemblies="@(AssemblyLoadContext.Default.Assemblies.Where(asm => asm.GetName().Name?.Contains("Boilerplate.Client") is true))"> <Found Context="routeData"> + <AppRouteDataPublisher RouteData="@routeData" /> <AuthorizeRouteView RouteData="@routeData"> <Authorizing> <LoadingComponent /> @@ -14,6 +15,7 @@ </AuthorizeRouteView> </Found> <NotFound> + <AppRouteDataPublisher RouteData="@null" /> <NotFoundPage /> </NotFound> <Navigating> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor.cs new file mode 100644 index 0000000000..411961fac0 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Routes.razor.cs @@ -0,0 +1,30 @@ +namespace Boilerplate.Client.Core.Components; + +public partial class Routes +{ + [AutoInject] NavigationManager? navigationManager { set => universalLinksNavigationManager = value; get => universalLinksNavigationManager; } + private static NavigationManager? universalLinksNavigationManager; + + public static async Task OpenUniversalLink(string url, bool forceLoad = false, bool replace = false) + { + await Task.Run(async () => + { + while (universalLinksNavigationManager is null) + { + await Task.Yield(); + } + }); + + if (CultureInfoManager.MultilingualEnabled && + forceLoad == false && + (AppPlatform.IsAndroid || AppPlatform.IsIOS)) + { + var currentCulture = CultureInfo.CurrentUICulture.Name; + var uri = new Uri(url); + var urlCulture = uri.GetCulture(); + forceLoad = urlCulture is not null && string.Equals(currentCulture, urlCulture, StringComparison.InvariantCultureIgnoreCase) is false; + } + + universalLinksNavigationManager!.NavigateTo(url, forceLoad, replace); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/StaticComponent.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/StaticComponent.cs index 47cb46d70d..32df52953a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/StaticComponent.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/StaticComponent.cs @@ -1,6 +1,4 @@ -using static System.Net.WebRequestMethods; - -namespace Boilerplate.Client.Core.Components; +namespace Boilerplate.Client.Core.Components; /// <summary> /// To prevent rendering recursion into a particular subtree use this component as base class. diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/_Imports.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/_Imports.razor similarity index 100% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/_Imports.razor rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/_Imports.razor index 5b78a959bb..d07a977b4c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/_Imports.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/_Imports.razor @@ -19,6 +19,6 @@ @using Boilerplate.Client.Core @using Boilerplate.Client.Core.Services @using Boilerplate.Client.Core.Components -@using Boilerplate.Client.Core.Components.Layout @using Boilerplate.Client.Core.Components.Pages +@using Boilerplate.Client.Core.Components.Layout @using Boilerplate.Client.Core.Services.Contracts diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs deleted file mode 100644 index 5738ce8a4f..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs +++ /dev/null @@ -1,76 +0,0 @@ -// <auto-generated /> -using System; -using Boilerplate.Client.Core.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Boilerplate.Client.Core.Data.Migrations -{ - [DbContext(typeof(OfflineDbContext))] - [Migration("20240729183448_InitialMigration")] - partial class InitialMigration - { - /// <inheritdoc /> - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.7"); - - modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property<long?>("BirthDate") - .HasColumnType("INTEGER"); - - b.Property<string>("Email") - .HasColumnType("TEXT"); - - b.Property<string>("FullName") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<int?>("Gender") - .HasColumnType("INTEGER"); - - b.Property<string>("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<string>("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property<string>("ProfileImageName") - .HasColumnType("TEXT"); - - b.Property<string>("UserName") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Users"); - - b.HasData( - new - { - Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), - BirthDate = 1306790461440000000L, - Email = "test@bitplatform.dev", - FullName = "Boilerplate test account", - Gender = 2, - Password = "123456", - PhoneNumber = "+31684207362", - UserName = "test" - }); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs deleted file mode 100644 index 707a8579b9..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable -#pragma warning disable DateTimeOffsetInsteadOfDateTimeAnalyzer - -namespace Boilerplate.Client.Core.Data.Migrations -{ - /// <inheritdoc /> - public partial class InitialMigration : Migration - { - /// <inheritdoc /> - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column<Guid>(type: "TEXT", nullable: false), - UserName = table.Column<string>(type: "TEXT", nullable: false), - Email = table.Column<string>(type: "TEXT", nullable: true), - PhoneNumber = table.Column<string>(type: "TEXT", nullable: true), - Password = table.Column<string>(type: "TEXT", nullable: false), - FullName = table.Column<string>(type: "TEXT", nullable: false), - Gender = table.Column<int>(type: "INTEGER", nullable: true), - BirthDate = table.Column<long>(type: "INTEGER", nullable: true), - ProfileImageName = table.Column<string>(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - - migrationBuilder.InsertData( - table: "Users", - columns: new[] { "Id", "BirthDate", "Email", "FullName", "Gender", "Password", "PhoneNumber", "ProfileImageName", "UserName" }, - values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, "test@bitplatform.dev", "Boilerplate test account", 2, "123456", "+31684207362", null, "test" }); - } - - /// <inheritdoc /> - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs new file mode 100644 index 0000000000..046b42efe0 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs @@ -0,0 +1,72 @@ +// <auto-generated /> +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Boilerplate.Client.Core.Data.Migrations; + +[DbContext(typeof(OfflineDbContext))] +[Migration("20241030140343_InitialMigration")] +partial class InitialMigration +{ + /// <inheritdoc /> + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<long?>("BirthDate") + .HasColumnType("INTEGER"); + + b.Property<string>("Email") + .HasColumnType("TEXT"); + + b.Property<string>("FullName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<int?>("Gender") + .HasColumnType("INTEGER"); + + b.Property<string>("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property<string>("ProfileImageName") + .HasColumnType("TEXT"); + + b.Property<string>("UserName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), + BirthDate = 1306790461440000000L, + Email = "test@bitplatform.dev", + FullName = "Boilerplate test account", + Gender = 0, + Password = "123456", + PhoneNumber = "+31684207362", + UserName = "test" + }); + }); +#pragma warning restore 612, 618 + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs new file mode 100644 index 0000000000..d859fcd8ef --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable +#pragma warning disable DateTimeOffsetInsteadOfDateTimeAnalyzer + +namespace Boilerplate.Client.Core.Data.Migrations; + +/// <inheritdoc /> +public partial class InitialMigration : Migration +{ + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + UserName = table.Column<string>(type: "TEXT", nullable: false), + Email = table.Column<string>(type: "TEXT", nullable: true), + PhoneNumber = table.Column<string>(type: "TEXT", nullable: true), + Password = table.Column<string>(type: "TEXT", nullable: false), + FullName = table.Column<string>(type: "TEXT", nullable: false), + Gender = table.Column<int>(type: "INTEGER", nullable: true), + BirthDate = table.Column<long>(type: "INTEGER", nullable: true), + ProfileImageName = table.Column<string>(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.InsertData( + table: "Users", + columns: new[] { "Id", "BirthDate", "Email", "FullName", "Gender", "Password", "PhoneNumber", "ProfileImageName", "UserName" }, + values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, "test@bitplatform.dev", "Boilerplate test account", 0, "123456", "+31684207362", null, "test" }); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Users"); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs index f06d1b796f..13cca205c7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs @@ -1,73 +1,69 @@ // <auto-generated /> -using System; -using Boilerplate.Client.Core.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Boilerplate.Client.Core.Data.Migrations +namespace Boilerplate.Client.Core.Data.Migrations; + +[DbContext(typeof(OfflineDbContext))] +partial class OfflineDbContextModelSnapshot : ModelSnapshot { - [DbContext(typeof(OfflineDbContext))] - partial class OfflineDbContextModelSnapshot : ModelSnapshot + protected override void BuildModel(ModelBuilder modelBuilder) { - protected override void BuildModel(ModelBuilder modelBuilder) - { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.7"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); - modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); - b.Property<long?>("BirthDate") - .HasColumnType("INTEGER"); + b.Property<long?>("BirthDate") + .HasColumnType("INTEGER"); - b.Property<string>("Email") - .HasColumnType("TEXT"); + b.Property<string>("Email") + .HasColumnType("TEXT"); - b.Property<string>("FullName") - .IsRequired() - .HasColumnType("TEXT"); + b.Property<string>("FullName") + .IsRequired() + .HasColumnType("TEXT"); - b.Property<int?>("Gender") - .HasColumnType("INTEGER"); + b.Property<int?>("Gender") + .HasColumnType("INTEGER"); - b.Property<string>("Password") - .IsRequired() - .HasColumnType("TEXT"); + b.Property<string>("Password") + .IsRequired() + .HasColumnType("TEXT"); - b.Property<string>("PhoneNumber") - .HasColumnType("TEXT"); + b.Property<string>("PhoneNumber") + .HasColumnType("TEXT"); - b.Property<string>("ProfileImageName") - .HasColumnType("TEXT"); + b.Property<string>("ProfileImageName") + .HasColumnType("TEXT"); - b.Property<string>("UserName") - .IsRequired() - .HasColumnType("TEXT"); + b.Property<string>("UserName") + .IsRequired() + .HasColumnType("TEXT"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("Users"); + b.ToTable("Users"); - b.HasData( - new - { - Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), - BirthDate = 1306790461440000000L, - Email = "test@bitplatform.dev", - FullName = "Boilerplate test account", - Gender = 2, - Password = "123456", - PhoneNumber = "+31684207362", - UserName = "test" - }); - }); + b.HasData( + new + { + Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), + BirthDate = 1306790461440000000L, + Email = "test@bitplatform.dev", + FullName = "Boilerplate test account", + Gender = 0, + Password = "123456", + PhoneNumber = "+31684207362", + UserName = "test" + }); + }); #pragma warning restore 612, 618 - } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ByteArrayExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ByteArrayExtensions.cs new file mode 100644 index 0000000000..d84ee60dd2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ByteArrayExtensions.cs @@ -0,0 +1,14 @@ +namespace System; + +public static class ByteArrayExtensions +{ + public static string ToStampString(this byte[]? source) + { + if (source is null or { Length: 0 }) + source = [0, 0, 0, 0, 0, 0, 0, 0]; + + var base64String = Convert.ToBase64String(source); + + return Uri.EscapeDataString(base64String); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs new file mode 100644 index 0000000000..b0b74618df --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs @@ -0,0 +1,155 @@ +//+:cnd:noEmit +//#if (offlineDb == true) +using Boilerplate.Client.Core.Data; +using Microsoft.EntityFrameworkCore; +//#endif +//#if (appInsights == true) +using BlazorApplicationInsights; +using BlazorApplicationInsights.Interfaces; +//#endif +using Boilerplate.Client.Core; +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components.WebAssembly.Services; +using Boilerplate.Client.Core.Components; +using Boilerplate.Client.Core.Services.HttpMessageHandlers; + +namespace Microsoft.Extensions.DependencyInjection; + +public static partial class IClientCoreServiceCollectionExtensions +{ + public static IServiceCollection AddClientCoreProjectServices(this IServiceCollection services, IConfiguration configuration) + { + // Services being registered here can get injected in client side (Web, Android, iOS, Windows, macOS) and server side (during pre rendering) + services.AddSharedProjectServices(configuration); + + services.AddTransient<IPrerenderStateService, NoopPrerenderStateService>(); + + services.AddScoped<ThemeService>(); + services.AddScoped<CultureService>(); + services.AddScoped<HttpClientHandler>(); + services.AddScoped<LazyAssemblyLoader>(); + services.AddScoped<IAuthTokenProvider, ClientSideAuthTokenProvider>(); + services.AddScoped<IExternalNavigationService, DefaultExternalNavigationService>(); + + // The following services must be unique to each app session. + // Defining them as singletons would result in them being shared across all users in Blazor Server and during pre-rendering. + // To address this, we use the AddSessioned extension method. + // AddSessioned applies AddSingleton in BlazorHybrid and AddScoped in Blazor WebAssembly and Blazor Server, ensuring correct service lifetimes for each environment. + services.AddSessioned<PubSubService>(); + services.AddSessioned<SnackBarService>(); + services.AddSessioned<MessageBoxService>(); + services.AddSessioned<ILocalHttpServer, NoopLocalHttpServer>(); + services.AddSessioned<ITelemetryContext, AppTelemetryContext>(); + services.AddSessioned<AuthenticationStateProvider, AuthenticationManager>(); + services.AddSessioned(sp => (AuthenticationManager)sp.GetRequiredService<AuthenticationStateProvider>()); + + services.AddSingleton(sp => configuration.Get<ClientCoreSettings>()!); + + services.AddOptions<ClientCoreSettings>() + .Bind(configuration) + .ValidateDataAnnotations() + .ValidateOnStart(); + + services.AddBitButilServices(); + services.AddBitBlazorUIServices(); + + // This code constructs a chain of HTTP message handlers. By default, it uses `HttpClientHandler` + // to send requests to the server. However, you can replace `HttpClientHandler` with other HTTP message + // handlers, such as ASP.NET Core's `HttpMessageHandler` from the Test Host, which is useful for integration tests. + services.AddScoped<HttpMessageHandlersChainFactory>(serviceProvider => transportHandler => + { + var constructedHttpMessageHandler = ActivatorUtilities.CreateInstance<RequestHeadersDelegationHandler>(serviceProvider, + [ActivatorUtilities.CreateInstance<AuthDelegatingHandler>(serviceProvider, + [ActivatorUtilities.CreateInstance<RetryDelegatingHandler>(serviceProvider, + [ActivatorUtilities.CreateInstance<ExceptionDelegatingHandler>(serviceProvider, [transportHandler])])])]); + return constructedHttpMessageHandler; + }); + services.AddScoped<AuthDelegatingHandler>(); + services.AddScoped<RetryDelegatingHandler>(); + services.AddScoped<ExceptionDelegatingHandler>(); + services.AddScoped<RequestHeadersDelegationHandler>(); + services.AddScoped(serviceProvider => + { + var transportHandler = serviceProvider.GetRequiredService<HttpClientHandler>(); + var constructedHttpMessageHandler = serviceProvider.GetRequiredService<HttpMessageHandlersChainFactory>().Invoke(transportHandler); + return constructedHttpMessageHandler; + }); + + //#if (offlineDb == true) + services.AddBesqlDbContextFactory<OfflineDbContext>(options => + { + var isRunningInsideDocker = Directory.Exists("/container_volume"); // Blazor Server - Docker (It's supposed to be a mounted volume named /container_volume) + var dirPath = isRunningInsideDocker ? "/container_volume" + : AppPlatform.IsBlazorHybridOrBrowser ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AC87AA5B-4B37-4E52-8468-2D5DF24AF256") + : Directory.GetCurrentDirectory(); // Blazor server (Non docker Linux, macOS or Windows) + + dirPath = Path.Combine(dirPath, "App_Data"); + + Directory.CreateDirectory(dirPath); + + var dbPath = Path.Combine(dirPath, "Offline.db"); + + options + // .UseModel(OfflineDbContextModel.Instance) + .UseSqlite($"Data Source={dbPath}"); + + options.EnableSensitiveDataLogging(AppEnvironment.IsDev()) + .EnableDetailedErrors(AppEnvironment.IsDev()); + }); + //#endif + + //#if (appInsights == true) + services.Add(ServiceDescriptor.Describe(typeof(IApplicationInsights), typeof(AppInsightsJsSdkService), AppPlatform.IsBrowser ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)); + services.AddBlazorApplicationInsights(x => + { + x.ConnectionString = configuration.Get<ClientCoreSettings>()!.ApplicationInsights?.ConnectionString; + }); + //#endif + + services.AddTypedHttpClients(); + + return services; + } + + internal static IServiceCollection AddSessioned<TService, TImplementation>(this IServiceCollection services) + where TImplementation : class, TService + where TService : class + { + if (AppPlatform.IsBlazorHybrid) + { + return services.AddSingleton<TService, TImplementation>(); + } + else + { + return services.AddScoped<TService, TImplementation>(); + } + } + + internal static IServiceCollection AddSessioned<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) + where TService : class + { + if (AppPlatform.IsBlazorHybrid) + { + services.Add(ServiceDescriptor.Singleton(implementationFactory)); + } + else + { + services.Add(ServiceDescriptor.Scoped(implementationFactory)); + } + + return services; + } + + internal static void AddSessioned<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceCollection services) + where TService : class + { + if (AppPlatform.IsBlazorHybrid) + { + services.AddSingleton<TService, TService>(); + } + else + { + services.AddScoped<TService, TService>(); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationBuilderExtensions.cs index 8e2c741fb1..f9e5310988 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationBuilderExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationBuilderExtensions.cs @@ -8,18 +8,26 @@ public static partial class IConfigurationBuilderExtensions /// <summary> /// Configuration priority (Lowest to highest) => /// Shared/appsettings.json - /// Shared/appsettings.Production.json + /// Shared/appsettings.{environment}.json (If present) /// Client/Core/appsettings.json - /// Client/Core/appsettings.Production.json - /// Server.Web and Server.Api only => + /// Client/Core/appsettings.{environment}.json (If present) + /// Server.Web (blazor server and pre-rendering) and/or Server.Api only => /// Server/appsettings.json - /// Server/appsettings.Production.json + /// Server/appsettings.{environment}.json (If present) /// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration#default-application-configuration-sources /// Blazor WebAssembly only => - /// Client/Web/appsettings.json (If present) - /// Client/Web/appsettings.Production.json (If present) + /// Client/Client.Web/appsettings.json + /// Client/Client.Web/appsettings.{environment}.json (If present) + /// Client/Client.Web/wwwroot/appsettings.json (If present) + /// Client/Client.Web/wwwroot/{environment}.json (If present) + /// Maui only => + /// Client/Client.Maui/appsettings.json + /// Client/Client.Maui/appsettings.{environment}.json (If present) + /// Windows only => + /// Client/Client.Windows/appsettings.json + /// Client/Client.Windows/appsettings.{environment}.json (If present) /// </summary> - public static void AddClientConfigurations(this IConfigurationBuilder builder) + public static void AddClientConfigurations(this IConfigurationBuilder builder, string clientEntryAssemblyName) { IConfigurationBuilder configBuilder = AppPlatform.IsBrowser ? new WebAssemblyHostConfiguration() : new ConfigurationBuilder(); @@ -33,7 +41,7 @@ public static void AddClientConfigurations(this IConfigurationBuilder builder) configBuilder.AddJsonStream(envSharedAppSettings); } - var clientCoreAssembly = Assembly.Load("Boilerplate.Client.Core"); + var clientCoreAssembly = AppDomain.CurrentDomain.GetAssemblies().Single(asm => asm.GetName()?.Name is "Boilerplate.Client.Core"); configBuilder.AddJsonStream(clientCoreAssembly.GetManifestResourceStream("Boilerplate.Client.Core.appsettings.json")!); @@ -43,6 +51,19 @@ public static void AddClientConfigurations(this IConfigurationBuilder builder) configBuilder.AddJsonStream(envClientCoreAppSettings); } + var clientEntryAssembly = Assembly.Load(clientEntryAssemblyName); + + if (clientEntryAssembly is not null) + { + configBuilder.AddJsonStream(clientEntryAssembly.GetManifestResourceStream($"{clientEntryAssemblyName}.appsettings.json")!); + + var envAppSettings = clientEntryAssembly.GetManifestResourceStream($"{clientEntryAssemblyName}.appsettings.{AppEnvironment.Current}.json"); + if (envAppSettings != null) + { + configBuilder.AddJsonStream(envAppSettings); + } + } + if (AppPlatform.IsBrowser) { var providersField = builder.GetType().GetField("_providers", BindingFlags.NonPublic | BindingFlags.Instance)!; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationExtensions.cs similarity index 57% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationExtensions.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationExtensions.cs index 723dc2cf28..1d4a1ad45c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IConfigurationExtensions.cs @@ -1,16 +1,13 @@ //-:cnd:noEmit + +using Boilerplate.Client.Core; + namespace Microsoft.Extensions.Configuration; public static partial class IConfigurationExtensions { - public static T GetRequiredValue<T>(this IConfiguration configuration, string name) - { - return (configuration ?? throw new ArgumentNullException(nameof(configuration))) - .GetValue<T>(name ?? throw new ArgumentNullException(nameof(name))) ?? throw new InvalidOperationException($"{name} config could not be found"); - } - public static string GetServerAddress(this IConfiguration configuration) { - var serverAddress = configuration.GetRequiredValue<string>("ServerAddress"); + var serverAddress = configuration.Get<ClientCoreSettings>()!.ServerAddress; if (AppEnvironment.IsDev() && serverAddress.Contains("localhost", StringComparison.InvariantCultureIgnoreCase) && @@ -24,11 +21,4 @@ public static string GetServerAddress(this IConfiguration configuration) ? serverAddress.TrimEnd('/') : throw new InvalidOperationException($"Api server address {serverAddress} is invalid"); } - - public static string GetWebClientUrl(this IConfiguration configuration) - { - var webClientUrl = configuration.GetValue<string?>("WebClientUrl"); - - return string.IsNullOrEmpty(webClientUrl) is false ? webClientUrl : configuration.GetServerAddress(); - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs index 75d8b4e9b7..57df98820e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs @@ -1,5 +1,8 @@ //+:cnd:noEmit using System.Reflection; +//#if (notification == true) +using Boilerplate.Shared.Dtos.PushNotification; +//#endif namespace Microsoft.JSInterop; @@ -10,6 +13,11 @@ public static ValueTask<string> GetBrowserPlatform(this IJSRuntime jsRuntime) return jsRuntime.InvokeAsync<string>("App.getPlatform"); } + public static ValueTask<string> GetTimeZone(this IJSRuntime jsRuntime) + { + return jsRuntime.InvokeAsync<string>("App.getTimeZone"); + } + public static ValueTask ApplyBodyElementClasses(this IJSRuntime jsRuntime, List<string> cssClasses, Dictionary<string, string> cssVariables) { return jsRuntime.InvokeVoidAsync("App.applyBodyElementClasses", cssClasses, cssVariables); @@ -27,12 +35,29 @@ public static ValueTask<string> GoogleRecaptchaReset(this IJSRuntime jsRuntime) } //#endif + //#if (notification == true) + public static async ValueTask<DeviceInstallationDto> GetDeviceInstallation(this IJSRuntime jsRuntime, string vapidPublicKey) + { + return await jsRuntime.InvokeAsync<DeviceInstallationDto>("App.getDeviceInstallation", vapidPublicKey); + } + //#endif + + /// <summary> + /// The return value would be false during pre-rendering + /// </summary> public static bool IsInitialized(this IJSRuntime jsRuntime) { - var type = jsRuntime.GetType(); + if (jsRuntime is null) + return false; - if (type.Name is not "RemoteJSRuntime") return true; // Blazor WASM/Hybrid + var type = jsRuntime.GetType(); - return (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)!; + return type.Name switch + { + "UnsupportedJavaScriptRuntime" => false, // pre-rendering + "RemoteJSRuntime" /* blazor server */ => (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)!, + "WebViewJSRuntime" /* blazor hybrid */ => type.GetField("_ipcSender", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(jsRuntime) is not null, + _ => true // blazor wasm + }; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ILoggingBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ILoggingBuilderExtensions.cs new file mode 100644 index 0000000000..474e6d82fa --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ILoggingBuilderExtensions.cs @@ -0,0 +1,34 @@ +using Boilerplate.Client.Core.Services.DiagnosticLog; + +namespace Microsoft.Extensions.Logging; + +public static class ILoggingBuilderExtensions +{ + public static ILoggingBuilder AddDiagnosticLogger(this ILoggingBuilder builder) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, DiagnosticLoggerProvider>()); + + return builder; + } + + public static ILoggingBuilder ConfigureLoggers(this ILoggingBuilder loggingBuilder) + { + loggingBuilder.ClearProviders(); + + if (AppEnvironment.IsDev()) + { + loggingBuilder.AddDebug(); + } + + if (!AppPlatform.IsBrowser) + { + loggingBuilder.AddConsole(); + // DiagnosticLogger is already logging in browser's console. + // But Console logger is still useful in Visual Studio's Device Log (Android, iOS) or BrowserStack etc. + } + + loggingBuilder.AddDiagnosticLogger(); + + return loggingBuilder; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 3b85b88318..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,140 +0,0 @@ -//+:cnd:noEmit -//#if (offlineDb == true) -using Boilerplate.Client.Core.Data; -using Microsoft.EntityFrameworkCore; -//#endif -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Components.WebAssembly.Services; -using Boilerplate.Client.Core.Services.HttpMessageHandlers; - -namespace Microsoft.Extensions.DependencyInjection; - -public static partial class IServiceCollectionExtensions -{ - public static IServiceCollection AddClientCoreProjectServices(this IServiceCollection services) - { - // Services being registered here can get injected in client side (Web, Android, iOS, Windows, macOS) and server side (during pre rendering) - - services.TryAddTransient<IPrerenderStateService, PrerenderStateService>(); - - services.TryAddSessioned<IPubSubService, PubSubService>(); - services.TryAddTransient<IAuthTokenProvider, ClientSideAuthTokenProvider>(); - services.TryAddTransient<IStorageService, BrowserStorageService>(); - services.TryAddSingleton<ILocalHttpServer, NoopLocalHttpServer>(); - services.TryAddTransient<IExternalNavigationService, DefaultExternalNavigationService>(); - - services.TryAddKeyedTransient<DelegatingHandler, RequestHeadersDelegationHandler>("DefaultMessageHandler"); - services.TryAddTransient<AuthDelegatingHandler>(); - services.TryAddTransient<RetryDelegatingHandler>(); - services.TryAddTransient<ExceptionDelegatingHandler>(); - services.TryAddSessioned<HttpClientHandler>(); - - services.AddSessioned<AuthenticationStateProvider, AuthenticationManager>(); // Use 'Add' instead of 'TryAdd' to override the aspnetcore's default AuthenticationStateProvider. - services.TryAddSessioned(sp => (AuthenticationManager)sp.GetRequiredService<AuthenticationStateProvider>()); - - services.TryAddSessioned<MessageBoxService>(); - services.TryAddTransient<LazyAssemblyLoader>(); - - services.AddBitButilServices(); - services.AddBitBlazorUIServices(); - - //#if (offlineDb == true) - services.AddBesqlDbContextFactory<OfflineDbContext>(options => - { - var isRunningInsideDocker = Directory.Exists("/container_volume"); // Blazor Server - Docker (It's supposed to be a mounted volume named /container_volume) - var dirPath = isRunningInsideDocker ? "/container_volume" - : AppPlatform.IsBlazorHybridOrBrowser ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AC87AA5B-4B37-4E52-8468-2D5DF24AF256") - : Directory.GetCurrentDirectory(); // Blazor server (Non docker Linux, macOS or Windows) - - dirPath = Path.Combine(dirPath, "App_Data"); - - Directory.CreateDirectory(dirPath); - - var dbPath = Path.Combine(dirPath, "Offline.db"); - - options - // .UseModel(OfflineDbContextModel.Instance) - .UseSqlite($"Data Source={dbPath}"); - - options.EnableSensitiveDataLogging(AppEnvironment.IsDev()) - .EnableDetailedErrors(AppEnvironment.IsDev()); - }); - //#endif - - services.AddTypedHttpClients(); - - services.AddSharedProjectServices(); - return services; - } - - /// <summary> - /// Utilizing the AddSessioned method seamlessly configures the service to function as a singleton in BlazorHybrid, and BlazorWebAssembly - /// environments. Simultaneously, it employs per-scope registration for pre-rendering and BlazorServer scenarios - /// </summary> - public static IServiceCollection AddSessioned<TService, TImplementation>(this IServiceCollection services) - where TImplementation : class, TService - where TService : class - { - if (AppPlatform.IsBlazorHybridOrBrowser) - { - return services.AddSingleton<TService, TImplementation>(); - } - else - { - return services.AddScoped<TService, TImplementation>(); - } - } - - /// <summary> - /// <inheritdoc cref="AddSessioned{TService, TImplementation}(IServiceCollection)"/> - /// </summary> - public static IServiceCollection TryAddSessioned<TService, TImplementation>(this IServiceCollection services) - where TImplementation : class, TService - where TService : class - { - if (AppPlatform.IsBlazorHybridOrBrowser) - { - services.TryAddSingleton<TService, TImplementation>(); - } - else - { - services.TryAddScoped<TService, TImplementation>(); - } - - return services; - } - - /// <summary> - /// <inheritdoc cref="AddSessioned{TService, TImplementation}(IServiceCollection)"/> - /// </summary> - public static IServiceCollection TryAddSessioned<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) - where TService : class - { - if (AppPlatform.IsBlazorHybridOrBrowser) - { - services.TryAdd(ServiceDescriptor.Singleton(implementationFactory)); - } - else - { - services.TryAdd(ServiceDescriptor.Scoped(implementationFactory)); - } - - return services; - } - - /// <summary> - /// <inheritdoc cref="AddSessioned{TService, TImplementation}(IServiceCollection)"/> - /// </summary> - public static void TryAddSessioned<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceCollection services) - where TService : class - { - if (AppPlatform.IsBlazorHybridOrBrowser) - { - services.TryAddSingleton<TService, TService>(); - } - else - { - services.TryAddScoped<TService, TService>(); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NavigationManagerExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NavigationManagerExtensions.cs index c1e1d1a8b9..6b7e621f42 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NavigationManagerExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NavigationManagerExtensions.cs @@ -1,74 +1,14 @@ -using System.Web; -using System.Text.RegularExpressions; - -namespace Microsoft.AspNetCore.Components; +namespace Microsoft.AspNetCore.Components; public static partial class NavigationManagerExtensions { public static string GetUriWithoutQueryParameter(this NavigationManager navigationManager, string key) { - var url = navigationManager.Uri; - - var uri = new Uri(url); - - // this gets all the query string key value pairs as a collection - var newQueryString = HttpUtility.ParseQueryString(uri.Query); - - // this removes the key if exists - newQueryString.Remove(key); - - // this gets the page path from root without QueryString - string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path); - - return newQueryString.Count > 0 - ? string.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString) - : pagePathWithoutQueryString; + return new Uri(navigationManager.Uri).GetUrlWithoutQueryParameter(key); } - /// <summary> - /// Reads culture from either route segment or query string. - /// https://adminpanel.bitpaltform.dev/en-US/categories - /// https://adminpanel.bitpaltform.dev/categories?culture=en-US - /// </summary> - public static string? GetCultureFromUri(this NavigationManager navigationManager) + public static string GetUriPath(this NavigationManager navigationManager) { - var url = navigationManager.Uri; - - var uri = new Uri(url); - - var culture = HttpUtility.ParseQueryString(uri.Query)["culture"]; - - if (string.IsNullOrEmpty(culture) is false) - return culture; - - var match = RouteDataRequestCulture().Match(uri.ToString()); - - try - { - CultureInfoManager.CreateCultureInfo(match.Value); - return match.Value; - } - catch { }; - - return null; - } - - [GeneratedRegex(@"([a-zA-Z]{2}-[a-zA-Z]{2})")] - public static partial Regex RouteDataRequestCulture(); - - public static string GetUriWithoutCulture(this NavigationManager navigationManager) - { - var uri = navigationManager.GetUriWithoutQueryParameter("culture"); - - var culture = navigationManager.GetCultureFromUri(); - - if (string.IsNullOrEmpty(culture) is false) - { - uri = uri - .Replace($"{culture}/", string.Empty) - .Replace(culture, string.Empty); - } - - return uri; + return new Uri(navigationManager.Uri).GetPath(); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NotificationExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NotificationExtensions.cs new file mode 100644 index 0000000000..09981a0aa1 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/NotificationExtensions.cs @@ -0,0 +1,16 @@ +namespace Bit.Butil; + +public static class NotificationExtensions +{ + public static async Task<bool> IsNotificationAvailable(this Notification notification) + { + var isPresent = await notification.IsSupported(); + if (isPresent) + { + if (await notification.GetPermission() is NotificationPermission.Granted) + return true; + return await notification.RequestPermission() is NotificationPermission.Granted; + } + return false; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor.cs deleted file mode 100644 index e6cdbad4b5..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Routes.razor.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Boilerplate.Client.Core; - -public partial class Routes -{ - [AutoInject] NavigationManager? navigationManager { set => universalLinksNavigationManager = value; get => universalLinksNavigationManager; } - public static NavigationManager? universalLinksNavigationManager; - - public static async Task OpenUniversalLink(string url, bool forceLoad = false, bool replace = false) - { - await Task.Run(async () => - { - while (universalLinksNavigationManager is null) - { - await Task.Yield(); - } - }); - - universalLinksNavigationManager!.NavigateTo(url, forceLoad, replace); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts index f009a5f682..106bade8b2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts @@ -1,4 +1,16 @@ -class App { +//+:cnd:noEmit +class App { + private static jsBridgeObj: DotNetObject; + + public static registerJsBridge(dotnetObj: DotNetObject) { + // For additional details, see the JsBridge.cs file. + App.jsBridgeObj = dotnetObj; + } + + public static showDiagnostic() { + return App.jsBridgeObj?.invokeMethodAsync('ShowDiagnostic'); + } + public static applyBodyElementClasses(cssClasses: string[], cssVariables: any): void { cssClasses?.forEach(c => document.body.classList.add(c)); Object.keys(cssVariables).forEach(key => document.body.style.setProperty(key, cssVariables[key])); @@ -7,10 +19,50 @@ public static getPlatform(): string { return (navigator as any).userAgentData?.platform || navigator?.platform; } + + public static getTimeZone(): string { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + } + + //#if (notification == true) + public static async getDeviceInstallation(vapidPublicKey: string) { + const registration = await navigator.serviceWorker.ready; + if (!registration) return null; + + const pushManager = registration.pushManager; + if (pushManager == null) return null; + + let subscription = await pushManager.getSubscription(); + if (subscription == null) { + subscription = await pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: vapidPublicKey + }); + } + const pushChannel = subscription.toJSON(); + const p256dh = pushChannel.keys!['p256dh']; + const auth = pushChannel.keys!['auth']; + return { installationId: `${p256dh}-${auth}`, platform: 'browser', p256dh: p256dh, auth: auth, endpoint: pushChannel.endpoint }; + }; + //#endif } declare class BitTheme { static init(options: any): void; }; +interface DotNetObject { + invokeMethod<T>(methodIdentifier: string, ...args: any[]): T; + invokeMethodAsync<T>(methodIdentifier: string, ...args: any[]): Promise<T>; + dispose(): void; +} + +window.addEventListener('load', setCssWindowSizes); +window.addEventListener('resize', setCssWindowSizes); + +function setCssWindowSizes() { + document.documentElement.style.setProperty('--win-width', `${window.innerWidth}px`); + document.documentElement.style.setProperty('--win-height', `${window.innerHeight}px`); +} + BitTheme.init({ system: true, onChange: (newTheme: string, oldThem: string) => { @@ -22,6 +74,6 @@ BitTheme.init({ document.body.classList.remove('theme-dark'); } const primaryBgColor = getComputedStyle(document.documentElement).getPropertyValue('--bit-clr-bg-pri'); - document.querySelector("meta[name=theme-color]")!.setAttribute('content', primaryBgColor); + document.querySelector('meta[name=theme-color]')!.setAttribute('content', primaryBgColor); } }); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppInsightsJsSdkService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppInsightsJsSdkService.cs new file mode 100644 index 0000000000..516158ac21 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppInsightsJsSdkService.cs @@ -0,0 +1,169 @@ +using BlazorApplicationInsights; +using BlazorApplicationInsights.Models; +using BlazorApplicationInsights.Interfaces; + +namespace Boilerplate.Client.Core.Services; + +public partial class AppInsightsJsSdkService : IApplicationInsights +{ + private TaskCompletionSource? telemetryInitializerIsAddedTcs; + private readonly TaskCompletionSource appInsightsJsFilesAreLoaded = new(); + + private readonly ApplicationInsights applicationInsights = new(); + + [AutoInject] private IJSRuntime jsRuntime = default!; + + public CookieMgr GetCookieMgr() + { + return applicationInsights.GetCookieMgr(); + } + + public void InitJSRuntime(IJSRuntime jSRuntime) + { + applicationInsights.InitJSRuntime(jSRuntime); + } + + public async Task ClearAuthenticatedUserContext() + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.ClearAuthenticatedUserContext(); + } + + public async Task<TelemetryContext> Context() + { + await EnsureApplicationInsightsIsReady(); + return await applicationInsights.Context(); + } + + public async Task Flush() + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.Flush(); + } + + public async Task SetAuthenticatedUserContext(string authenticatedUserId, string? accountId = null, bool? storeInCookie = null) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.SetAuthenticatedUserContext(authenticatedUserId, accountId, storeInCookie); + } + + public async Task StartTrackEvent(string name) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.StartTrackEvent(name); + } + + public async Task StartTrackPage(string? name = null) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.StartTrackPage(name); + } + + public async Task StopTrackEvent(string name, Dictionary<string, object?>? properties = null, Dictionary<string, decimal>? measurements = null) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.StopTrackEvent(name, properties, measurements); + } + + public async Task StopTrackPage(string? name = null, string? url = null, Dictionary<string, object?>? customProperties = null, Dictionary<string, decimal>? measurements = null) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.StopTrackPage(name, url, customProperties, measurements); + } + + public async Task TrackDependencyData(DependencyTelemetry dependency) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackDependencyData(dependency); + } + + public async Task TrackEvent(EventTelemetry @event) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackEvent(@event); + } + + public async Task TrackException(ExceptionTelemetry exception) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackException(exception); + } + + public async Task TrackMetric(MetricTelemetry metric) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackMetric(metric); + } + + public async Task TrackPageView(PageViewTelemetry? pageView = null) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackPageView(pageView); + } + + public async Task TrackPageViewPerformance(PageViewPerformanceTelemetry pageViewPerformance) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackPageViewPerformance(pageViewPerformance); + } + + public async Task TrackTrace(TraceTelemetry trace) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.TrackTrace(trace); + } + + public async Task UpdateCfg(Config newConfig, bool mergeExisting = true) + { + await EnsureApplicationInsightsIsReady(); + await applicationInsights.UpdateCfg(newConfig, mergeExisting); + } + + public async Task AddTelemetryInitializer(TelemetryItem telemetryItem) + { + await EnsureAppInsightsJsFilesAreLoaded(); + try + { + await applicationInsights.AddTelemetryInitializer(telemetryItem); + telemetryInitializerIsAddedTcs!.SetResult(); + } + catch (Exception exp) + { + telemetryInitializerIsAddedTcs!.SetException(exp); + } + } + + private async Task EnsureApplicationInsightsIsReady() + { + await appInsightsJsFilesAreLoaded.Task; + await telemetryInitializerIsAddedTcs!.Task; + } + + private async Task EnsureAppInsightsJsFilesAreLoaded() + { + try + { + if (telemetryInitializerIsAddedTcs is not null) + return; + + telemetryInitializerIsAddedTcs = new(); + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + while (true) + { + if (await jsRuntime.InvokeAsync<bool>("window.hasOwnProperty", "appInsights") && + await jsRuntime.InvokeAsync<bool>("window.hasOwnProperty", "blazorApplicationInsights")) + { + appInsightsJsFilesAreLoaded.SetResult(); + break; + } + await Task.Delay(250, cts.Token); + } + } + catch (Exception exp) + { + appInsightsJsFilesAreLoaded.SetException(exp); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppPlatform.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppPlatform.cs index b626ff68f6..4c451e99bc 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppPlatform.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppPlatform.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace Boilerplate.Client.Core.Services; @@ -8,12 +8,24 @@ public static partial class AppPlatform public static bool IsBlazorHybridOrBrowser => IsBlazorHybrid || IsBrowser; + [SupportedOSPlatformGuard("android")] public static bool IsAndroid => OperatingSystem.IsAndroid(); - public static bool IsIOS => OperatingSystem.IsIOS(); + + [SupportedOSPlatformGuard("ios")] + public static bool IsIOS => OperatingSystem.IsIOS() && !IsIosOnMacOS; + + [SupportedOSPlatformGuard("windows")] public static bool IsWindows => OperatingSystem.IsWindows(); + + /// <summary> + /// Blazor WebAssembly + /// </summary> + [SupportedOSPlatformGuard("browser")] public static bool IsBrowser => OperatingSystem.IsBrowser(); + + [SupportedOSPlatformGuard("macOS")] public static bool IsMacOS => OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || IsIosOnMacOS; - public static bool IsIosOnMacOS { get; set; } - public static string OSDescription { get; set; } = RuntimeInformation.OSDescription; + [SupportedOSPlatformGuard("ios")] + public static bool IsIosOnMacOS { get; set; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppRenderMode.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppRenderMode.cs deleted file mode 100644 index 21d9c2995a..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppRenderMode.cs +++ /dev/null @@ -1,31 +0,0 @@ -//-:cnd:noEmit -using Microsoft.AspNetCore.Components.Web; - -namespace Boilerplate.Client.Core.Services; - -public static partial class AppRenderMode -{ - public static readonly bool PrerenderEnabled = false; - - // https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes#render-modes - public static IComponentRenderMode BlazorAuto { get; } = new InteractiveAutoRenderMode(PrerenderEnabled); - public static IComponentRenderMode BlazorWebAssembly { get; } = new InteractiveWebAssemblyRenderMode(PrerenderEnabled); - public static IComponentRenderMode BlazorServer { get; } = new InteractiveServerRenderMode(PrerenderEnabled); - public static IComponentRenderMode? StaticSsr { get; } = null /*Pre-rendering without interactivity*/; - public static IComponentRenderMode NoPrerenderBlazorWebAssembly => new InteractiveWebAssemblyRenderMode(prerender: false); - - public static IComponentRenderMode? Current => - AppEnvironment.IsDev() - ? BlazorServer // For better development experience. - : BlazorAuto; // For better production experience. - - /// <summary> - /// To enable/disable pwa support, navigate to Directory.Build.props and modify the PwaEnabled flag. - /// </summary> - public static bool PwaEnabled { get; } = -#if PwaEnabled - true; -#else - false; -#endif -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs new file mode 100644 index 0000000000..b7b071834c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs @@ -0,0 +1,29 @@ +//+:cnd:noEmit +using System.Runtime.InteropServices; + +namespace Boilerplate.Client.Core.Services; + +public class AppTelemetryContext : ITelemetryContext +{ + public virtual Guid? UserId { get; set; } + + public virtual Guid? UserSessionId { get; set; } + + public Guid AppSessionId { get; set; } = Guid.NewGuid(); + + public virtual string? OS { get; set; } = RuntimeInformation.OSDescription; + + public virtual string? AppVersion { get; set; } = typeof(AppTelemetryContext).Assembly.GetName().Version?.ToString(); + + public virtual string? WebView { get; set; } + + public virtual string? UserAgent { get; set; } + + public string? TimeZone { get; set; } + + public string? Culture { get; set; } = CultureInfo.CurrentCulture.Name; + + public string? Environment { get; set; } = AppEnvironment.Current; + + public bool IsOnline { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppThemeType.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppThemeType.cs new file mode 100644 index 0000000000..7c58a3f5bc --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppThemeType.cs @@ -0,0 +1,7 @@ +namespace Boilerplate.Client.Core.Services; + +public enum AppThemeType +{ + Light, + Dark +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs index cb8793115e..897886353e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs @@ -1,22 +1,28 @@ -using System.Text; -using System.Text.Json; -using Boilerplate.Shared.Dtos.Identity; +using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Shared.Controllers.Identity; namespace Boilerplate.Client.Core.Services; public partial class AuthenticationManager : AuthenticationStateProvider { + /// <summary> + /// To prevent multiple simultaneous refresh token requests. + /// </summary> + private readonly SemaphoreSlim semaphore = new(1, maxCount: 1); + [AutoInject] private Cookie cookie = default!; - [AutoInject] private IAuthTokenProvider tokenProvider = default!; + [AutoInject] private IJSRuntime jsRuntime = default!; [AutoInject] private IStorageService storageService = default!; - [AutoInject] private IIdentityController identityController = default!; [AutoInject] private IUserController userController = default!; - [AutoInject] private IStringLocalizer<AppStrings> localizer = default!; - [AutoInject] private JsonSerializerOptions jsonSerializerOptions = default!; - [AutoInject] private IExceptionHandler exceptionHandler = default!; + [AutoInject] private IAuthTokenProvider tokenProvider = default!; [AutoInject] private IPrerenderStateService prerenderStateService; + [AutoInject] private IExceptionHandler exceptionHandler = default!; + [AutoInject] private IIdentityController identityController = default!; + /// <summary> + /// Sign in and return whether the user requires two-factor authentication. + /// </summary> + /// <returns>true if the user requires two-factor authentication; otherwise, false.</returns> public async Task<bool> SignIn(SignInRequestDto request, CancellationToken cancellationToken) { var response = await identityController.SignIn(request, cancellationToken); @@ -72,44 +78,51 @@ public override async Task<AuthenticationState> GetAuthenticationStateAsync() { try { - var access_token = await prerenderStateService.GetValue(() => tokenProvider.GetAccessTokenAsync()); + var access_token = await prerenderStateService.GetValue(() => tokenProvider.GetAccessToken()); - if (string.IsNullOrEmpty(access_token) && tokenProvider.IsInitialized) - { - string? refresh_token = await storageService.GetItem("refresh_token"); + bool inPrerenderSession = AppPlatform.IsBlazorHybrid is false && jsRuntime.IsInitialized() is false; - if (string.IsNullOrEmpty(refresh_token) is false) + if (string.IsNullOrEmpty(access_token) && inPrerenderSession is false) + { + try { - // We refresh the access_token to ensure a seamless user experience, preventing unnecessary 'NotAuthorized' page redirects and improving overall UX. - // This method is triggered after 401 and 403 server responses in AuthDelegationHandler, - // as well as when accessing pages without the required permissions in NotAuthorizedPage, ensuring that any recent claims granted to the user are promptly reflected. - - try + await semaphore.WaitAsync(); + access_token = await tokenProvider.GetAccessToken(); + if (string.IsNullOrEmpty(access_token)) // Check again after acquiring the lock. { - var refreshTokenResponse = await identityController.Refresh(new() { RefreshToken = refresh_token }, CancellationToken.None); - await StoreTokens(refreshTokenResponse!); - access_token = refreshTokenResponse!.AccessToken; - } - catch (UnauthorizedException) // refresh_token is either invalid or expired. - { - await storageService.RemoveItem("refresh_token"); + string? refresh_token = await storageService.GetItem("refresh_token"); + + if (string.IsNullOrEmpty(refresh_token) is false) + { + // We refresh the access_token to ensure a seamless user experience, preventing unnecessary 'NotAuthorized' page redirects and improving overall UX. + // This method is triggered after 401 and 403 server responses in AuthDelegationHandler, + // as well as when accessing pages without the required permissions in NotAuthorizedPage, ensuring that any recent claims granted to the user are promptly reflected. + + try + { + var refreshTokenResponse = await identityController.Refresh(new() { RefreshToken = refresh_token }, CancellationToken.None); + await StoreTokens(refreshTokenResponse!); + access_token = refreshTokenResponse!.AccessToken; + } + catch (UnauthorizedException) // refresh_token is either invalid or expired. + { + await storageService.RemoveItem("refresh_token"); + } + } } } + finally + { + semaphore.Release(); + } } - if (string.IsNullOrEmpty(access_token)) - { - return NotSignedIn(); - } - - var identity = new ClaimsIdentity(claims: ParseTokenClaims(access_token), authenticationType: "Bearer", nameType: "name", roleType: "role"); - - return new AuthenticationState(new ClaimsPrincipal(identity)); + return new AuthenticationState(tokenProvider.ParseAccessToken(access_token, validateExpiry: false /* For better UX in order to minimize Routes.razor's Authorizing loading duration. */)); } catch (Exception exp) { exceptionHandler.Handle(exp); // Do not throw exceptions in GetAuthenticationStateAsync. This will fault CascadingAuthenticationState's state unless NotifyAuthenticationStateChanged is called again. - return NotSignedIn(); + return new AuthenticationState(tokenProvider.Anonymous()); } } @@ -123,7 +136,7 @@ private async Task StoreTokens(TokenResponseDto response, bool? rememberMe = nul await storageService.SetItem("access_token", response!.AccessToken, rememberMe is true); await storageService.SetItem("refresh_token", response!.RefreshToken, rememberMe is true); - if (AppRenderMode.PrerenderEnabled && AppPlatform.IsBlazorHybrid is false) + if (AppPlatform.IsBlazorHybrid is false && jsRuntime.IsInitialized()) { await cookie.Set(new() { @@ -136,51 +149,4 @@ await cookie.Set(new() }); } } - - private static AuthenticationState NotSignedIn() - { - return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); - } - - private IEnumerable<Claim> ParseTokenClaims(string access_token) - { - return ParseJwt(access_token) - .Select(keyValue => new Claim(keyValue.Key, keyValue.Value.ToString() ?? string.Empty)) - .ToArray(); - } - - private Dictionary<string, object> ParseJwt(string access_token) - { - // Split the token to get the payload - string base64UrlPayload = access_token.Split('.')[1]; - - // Convert the payload from Base64Url format to Base64 - string base64Payload = ConvertBase64UrlToBase64(base64UrlPayload); - - // Decode the Base64 string to get a JSON string - string jsonPayload = Encoding.UTF8.GetString(Convert.FromBase64String(base64Payload)); - - // Deserialize the JSON string to a dictionary - var claims = JsonSerializer.Deserialize(jsonPayload, jsonSerializerOptions.GetTypeInfo<Dictionary<string, object>>())!; - - return claims; - } - - private static string ConvertBase64UrlToBase64(string base64Url) - { - base64Url = base64Url.Replace('-', '+').Replace('_', '/'); - - // Adjust base64Url string length for padding - switch (base64Url.Length % 4) - { - case 2: - base64Url += "=="; - break; - case 3: - base64Url += "="; - break; - } - - return base64Url; - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs new file mode 100644 index 0000000000..83a9d3b13a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs @@ -0,0 +1,20 @@ +//+:cnd:noEmit +namespace Boilerplate.Client.Core.Services; + +public static partial class ClientPubSubMessages +{ + public const string SHOW_SNACK = nameof(SHOW_SNACK); + public const string SHOW_MESSAGE = nameof(SHOW_MESSAGE); + + public const string THEME_CHANGED = nameof(THEME_CHANGED); + public const string OPEN_NAV_PANEL = nameof(OPEN_NAV_PANEL); + public const string CULTURE_CHANGED = nameof(CULTURE_CHANGED); + public const string PROFILE_UPDATED = nameof(PROFILE_UPDATED); + public const string IS_ONLINE_CHANGED = nameof(IS_ONLINE_CHANGED); + public const string PAGE_TITLE_CHANGED = nameof(PAGE_TITLE_CHANGED); + public const string ROUTE_DATA_UPDATED = nameof(ROUTE_DATA_UPDATED); + public const string UPDATE_IDENTITY_HEADER_BACK_LINK = nameof(UPDATE_IDENTITY_HEADER_BACK_LINK); + public const string IDENTITY_HEADER_BACK_LINK_CLICKED = nameof(IDENTITY_HEADER_BACK_LINK_CLICKED); + + public const string SHOW_DIAGNOSTIC_MODAL = nameof(SHOW_DIAGNOSTIC_MODAL); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientSideAuthTokenProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientSideAuthTokenProvider.cs index 6ffd9b1dcc..12a672dc4f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientSideAuthTokenProvider.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientSideAuthTokenProvider.cs @@ -5,9 +5,7 @@ public partial class ClientSideAuthTokenProvider : IAuthTokenProvider { [AutoInject] private IStorageService storageService = default!; - public bool IsInitialized => true; - - public async Task<string?> GetAccessTokenAsync() + public async Task<string?> GetAccessToken() { return await storageService.GetItem("access_token"); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs index 8367fdb592..e6d48dab95 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs @@ -1,7 +1,92 @@ -namespace Boilerplate.Client.Core.Services.Contracts; +using System.Text; + +namespace Boilerplate.Client.Core.Services.Contracts; public interface IAuthTokenProvider { - bool IsInitialized { get; } - Task<string?> GetAccessTokenAsync(); + Task<string?> GetAccessToken(); + + public ClaimsPrincipal Anonymous() => new(new ClaimsIdentity()); + + public ClaimsPrincipal ParseAccessToken(string? access_token, bool validateExpiry) + { + if (string.IsNullOrEmpty(access_token) is true) + return Anonymous(); + + var claims = ReadClaims(access_token, validateExpiry); + + if (claims is null) + return Anonymous(); + + var identity = new ClaimsIdentity(claims: claims, authenticationType: "Bearer", nameType: "name", roleType: "role"); + + var claimPrinciple = new ClaimsPrincipal(identity); + + return claimPrinciple; + } + + private IEnumerable<Claim>? ReadClaims(string access_token, bool validateExpiry) + { + var parsedClaims = DeserializeAccessToken(access_token); + + if (validateExpiry && long.TryParse(parsedClaims["exp"].ToString(), out var expSeconds)) + { + var expirationDate = DateTimeOffset.FromUnixTimeSeconds(expSeconds); + if (expirationDate <= DateTimeOffset.UtcNow) + return null; + } + + var claims = new List<Claim>(); + foreach (var keyValue in parsedClaims) + { + if (keyValue.Value.ValueKind == JsonValueKind.Array) + { + foreach (var element in keyValue.Value.EnumerateArray()) + { + claims.Add(new Claim(keyValue.Key, element.ToString() ?? string.Empty)); + } + } + else + { + claims.Add(new Claim(keyValue.Key, keyValue.Value.ToString() ?? string.Empty)); + } + } + + return claims; + } + + private Dictionary<string, JsonElement> DeserializeAccessToken(string access_token) + { + // Split the token to get the payload + string base64UrlPayload = access_token.Split('.')[1]; + + // Convert the payload from Base64Url format to Base64 + string base64Payload = ConvertBase64UrlToBase64(base64UrlPayload); + + // Decode the Base64 string to get a JSON string + string jsonPayload = Encoding.UTF8.GetString(Convert.FromBase64String(base64Payload)); + + // Deserialize the JSON string to a dictionary + var claims = JsonSerializer.Deserialize(jsonPayload, AppJsonContext.Default.Options.GetTypeInfo<Dictionary<string, JsonElement>>())!; + + return claims; + } + + private string ConvertBase64UrlToBase64(string base64Url) + { + base64Url = base64Url.Replace('-', '+').Replace('_', '/'); + + // Adjust base64Url string length for padding + switch (base64Url.Length % 4) + { + case 2: + base64Url += "=="; + break; + case 3: + base64Url += "="; + break; + } + + return base64Url; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IBitDeviceCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IBitDeviceCoordinator.cs index c10fb5895d..65edf8dd5d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IBitDeviceCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IBitDeviceCoordinator.cs @@ -5,7 +5,5 @@ /// </summary> public interface IBitDeviceCoordinator { - public double GetStatusBarHeight() { return 0; } - public async Task ApplyTheme(bool isDark) { } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IExceptionHandler.cs index 0ae5ef549b..452b62a49c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IExceptionHandler.cs @@ -1,6 +1,12 @@ -namespace Boilerplate.Client.Core.Services.Contracts; +using System.Runtime.CompilerServices; + +namespace Boilerplate.Client.Core.Services.Contracts; public interface IExceptionHandler { - void Handle(Exception exception, IDictionary<string, object?>? parameters = null); + void Handle(Exception exception, + Dictionary<string, object?>? parameters = null, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = ""); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPubSubService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPubSubService.cs deleted file mode 100644 index 74e9aebace..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPubSubService.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Boilerplate.Client.Core.Services.Contracts; - -/// <summary> -/// Contract for Publish/Subscribe pattern. -/// </summary> -public interface IPubSubService -{ - void Publish(string message, object? payload); - Action Subscribe(string message, Func<object?, Task> handler); -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPushNotificationService.cs new file mode 100644 index 0000000000..ffba49b061 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IPushNotificationService.cs @@ -0,0 +1,11 @@ +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Core.Services.Contracts; + +public interface IPushNotificationService +{ + string Token { get; set; } + Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken); + Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken); + Task RegisterDevice(CancellationToken cancellationToken); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs new file mode 100644 index 0000000000..a1b5962c34 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs @@ -0,0 +1,72 @@ +//+:cnd:noEmit +namespace Boilerplate.Client.Core.Services.Contracts; + +public interface ITelemetryContext +{ + private static ITelemetryContext? _current; + + public static ITelemetryContext? Current + { + get + { + if (AppPlatform.IsBlazorHybridOrBrowser is false) + throw new InvalidOperationException("ITelemetryContext.Current is only available in Blazor Hybrid or web assembly apps."); + + return _current; + } + set + { + if (AppPlatform.IsBlazorHybridOrBrowser is false) + throw new InvalidOperationException("ITelemetryContext.Current is only available in Blazor Hybrid or web assembly apps."); + + _current = value; + } + } + + public Guid? UserId { get; set; } + + /// <summary> + /// Stored in Users table's Sessions column and is identified after the user sign-in. + /// </summary> + public Guid? UserSessionId { get; set; } + + public Guid AppSessionId { get; set; } + + public string? OS { get; set; } + + public string? AppVersion { get; set; } + public string? WebView { get; set; } + + public string? UserAgent { get; set; } + + public string? TimeZone { get; set; } + public string? Culture { get; set; } + + public string? Environment { get; set; } + + public bool IsOnline { get; set; } + + public Dictionary<string, object?> ToDictionary(Dictionary<string, object?>? additionalParameters = null) + { + var data = new Dictionary<string, object?>(additionalParameters ??= []) + { + { nameof(UserId), UserId }, + { nameof(UserSessionId), UserSessionId }, + { nameof(AppSessionId), AppSessionId }, + { nameof(OS), OS }, + { nameof(AppVersion), AppVersion }, + { nameof(UserAgent), UserAgent }, + { nameof(TimeZone), TimeZone }, + { nameof(Culture), Culture }, + { nameof(Environment), Environment }, + { nameof(IsOnline), IsOnline } + }; + + if (AppPlatform.IsBlazorHybrid) + { + data[nameof(WebView)] = WebView; + } + + return data; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/CultureService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/CultureService.cs new file mode 100644 index 0000000000..2722fdf4c7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/CultureService.cs @@ -0,0 +1,32 @@ +namespace Boilerplate.Client.Core.Services; + +public partial class CultureService +{ + [AutoInject] private Cookie cookie = default!; + [AutoInject] private PubSubService pubSubService = default!; + [AutoInject] private IStorageService storageService = default!; + [AutoInject] private NavigationManager navigationManager = default!; + [AutoInject] private CultureInfoManager cultureInfoManager = default!; + + public async Task ChangeCulture(string? cultureName) + { + if (AppPlatform.IsBlazorHybrid) + { + await storageService.SetItem("Culture", cultureName, persistent: true); + cultureInfoManager.SetCurrentCulture(cultureName!); + pubSubService.Publish(ClientPubSubMessages.CULTURE_CHANGED, cultureName); + } + else + { + await cookie.Set(new() + { + MaxAge = 30 * 24 * 3600, + Name = ".AspNetCore.Culture", + Secure = AppEnvironment.IsDev() is false, + Value = Uri.EscapeDataString($"c={cultureName}|uic={cultureName}"), + }); + } + + navigationManager.NavigateTo(new Uri(navigationManager.Uri).GetUrlWithoutCulture(), forceLoad: true, replace: true); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLog.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLog.cs new file mode 100644 index 0000000000..a3b76bbb0a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLog.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Services.DiagnosticLog; + +public class DiagnosticLog +{ + public DateTimeOffset CreatedOn { get; set; } + + public LogLevel Level { get; set; } + + public string? Message { get; set; } + + public Exception? Exception { get; set; } + + public IDictionary<string, string?>? State { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLogger.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLogger.cs new file mode 100644 index 0000000000..9f25214154 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLogger.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Services.DiagnosticLog; + +public partial class DiagnosticLogger : ILogger, IDisposable +{ + public static ConcurrentBag<DiagnosticLog> Store { get; } = []; + + private IDictionary<string, object?>? currentState; + + public string? CategoryName { get; set; } + + public IDisposable? BeginScope<TState>(TState state) + where TState : notnull + { + if (state is IDictionary<string, object?> data) + { + currentState = data; + } + + return this; + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel != LogLevel.None; + } + + public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) + { + if (IsEnabled(logLevel) is false) return; + + var message = formatter(state, exception); + + Store.Add(new() { CreatedOn = DateTimeOffset.Now, Level = logLevel, Message = message, Exception = exception, State = currentState?.ToDictionary(i => i.Key, i => i.Value?.ToString()) }); + } + + public void Dispose() + { + + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLoggerProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLoggerProvider.cs new file mode 100644 index 0000000000..74b2cccbd9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DiagnosticLog/DiagnosticLoggerProvider.cs @@ -0,0 +1,24 @@ +//+:cnd:noEmit +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Services.DiagnosticLog; + +// https://learn.microsoft.com/en-us/aspnet/core/blazor/hybrid/developer-tools + +/// <summary> +/// Provides a custom logger that outputs log messages to the browser's console and allows for selective display of logs +/// within the application UI for enhanced diagnostics. +/// </summary> +[ProviderAlias("DiagnosticLogger")] +public partial class DiagnosticLoggerProvider : ILoggerProvider +{ + public ILogger CreateLogger(string categoryName) + { + return new DiagnosticLogger() + { + CategoryName = categoryName + }; + } + + public void Dispose() { } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs index 6194fe093e..6c137be287 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs @@ -1,54 +1,92 @@ -//-:cnd:noEmit +//+:cnd:noEmit +using System.Reflection; using System.Diagnostics; -using System.Text; using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; namespace Boilerplate.Client.Core.Services; public abstract partial class ExceptionHandlerBase : IExceptionHandler { - [AutoInject] protected readonly IStringLocalizer<AppStrings> Localizer = default!; - [AutoInject] protected readonly MessageBoxService MessageBoxService = default!; [AutoInject] protected Bit.Butil.Console Console = default!; + [AutoInject] protected ITelemetryContext TelemetryContext = default!; [AutoInject] protected ILogger<ExceptionHandlerBase> Logger = default!; + [AutoInject] protected readonly MessageBoxService MessageBoxService = default!; + [AutoInject] protected readonly IStringLocalizer<AppStrings> Localizer = default!; - public void Handle(Exception exp, IDictionary<string, object?>? parameters = null) + public void Handle(Exception exception, + Dictionary<string, object?>? parameters = null, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") { - if (exp is TaskCanceledException) - return; + parameters = TelemetryContext.ToDictionary(parameters); - parameters ??= new Dictionary<string, object?>(); + parameters[nameof(filePath)] = filePath; + parameters[nameof(memberName)] = memberName; + parameters[nameof(lineNumber)] = lineNumber; - Handle(exp, parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty)); + Handle(exception, parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty)); } protected virtual void Handle(Exception exception, Dictionary<string, object> parameters) { - var isDebug = AppEnvironment.IsDev(); + var isDevEnv = AppEnvironment.IsDev(); - string exceptionMessage = (exception as KnownException)?.Message ?? - (isDebug ? exception.ToString() : Localizer[nameof(AppStrings.UnknownException)]); - - if (isDebug) + using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty))) { - if (AppPlatform.IsBlazorHybrid) + var exceptionMessageToLog = exception.Message; + var innerException = exception.InnerException; + + while (innerException is not null) { - StringBuilder errorInfo = new(); - errorInfo.AppendLine(exceptionMessage); - foreach (var item in parameters) - { - errorInfo.AppendLine($"{item.Key}: {item.Value}"); - } - _ = Console.Error(errorInfo.ToString()); + exceptionMessageToLog += $"{Environment.NewLine}{innerException.Message}"; + innerException = innerException.InnerException; } + + if (exception is KnownException) + { + Logger.LogError(exception, exceptionMessageToLog); + } + else + { + Logger.LogCritical(exception, exceptionMessageToLog); + } + } + + string exceptionMessageToShow = (exception as KnownException)?.Message ?? + (isDevEnv ? exception.ToString() : Localizer[nameof(AppStrings.UnknownException)]); + + MessageBoxService.Show(exceptionMessageToShow, Localizer[nameof(AppStrings.Error)]); + + if (isDevEnv) + { Debugger.Break(); } + } - using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty))) + protected Exception UnWrapException(Exception exception) + { + if (exception is AggregateException aggregateException) { - Logger.LogError(exception, exceptionMessage); + return aggregateException.Flatten().InnerException ?? aggregateException; } + else if (exception is TargetInvocationException) + { + return exception.InnerException ?? exception; + } + + return exception; + } + + protected bool IgnoreException(Exception exception) + { + if (exception is KnownException) + return false; - _ = MessageBoxService.Show(exceptionMessage, Localizer[nameof(AppStrings.Error)]); + return exception is TaskCanceledException || + exception is OperationCanceledException || + exception is TimeoutException || + (exception.InnerException is not null && IgnoreException(exception.InnerException)); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs index c63deef0d8..c796951373 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs @@ -1,25 +1,33 @@ -using System.Reflection; -using System.Net.Http.Headers; -using Boilerplate.Shared.Controllers; +using System.Net.Http.Headers; +using Boilerplate.Shared.Controllers.Identity; namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class AuthDelegatingHandler(IAuthTokenProvider tokenProvider, IServiceProvider serviceProvider, IStorageService storageService, RetryDelegatingHandler handler) +public partial class AuthDelegatingHandler(IAuthTokenProvider tokenProvider, + IJSRuntime jsRuntime, + IServiceProvider serviceProvider, + IStorageService storageService, + HttpMessageHandler handler) : DelegatingHandler(handler) { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - if (request.Headers.Authorization is null) + var isRefreshTokenRequest = request.RequestUri?.LocalPath?.Contains(IIdentityController.RefreshUri, StringComparison.InvariantCultureIgnoreCase) is true; + + try { - var access_token = await tokenProvider.GetAccessTokenAsync(); - if (access_token is not null) + if (request.Headers.Authorization is null && isRefreshTokenRequest is false) { - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", access_token); + var access_token = await tokenProvider.GetAccessToken(); + if (access_token is not null) + { + if (tokenProvider.ParseAccessToken(access_token, validateExpiry: true).IsAuthenticated() is false) + throw new UnauthorizedException(nameof(AppStrings.YouNeedToSignIn)); + + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", access_token); + } } - } - try - { return await base.SendAsync(request, cancellationToken); } catch (KnownException _) when (_ is ForbiddenException or UnauthorizedException) @@ -27,18 +35,21 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage // Let's update the access token by refreshing it when a refresh token is available. // Following this procedure, the newly acquired access token may now include the necessary roles or claims. - if (tokenProvider.IsInitialized is false || - request.RequestUri?.LocalPath?.Contains("api/Identity/Refresh", StringComparison.InvariantCultureIgnoreCase) is true /* To prevent refresh token loop */) throw; + if (AppPlatform.IsBlazorHybrid is false && jsRuntime.IsInitialized() is false) + throw; // We don't have access to refresh_token during pre-rendering. - var authManager = serviceProvider.GetRequiredService<AuthenticationManager>(); - var refresh_token = await storageService.GetItem("refresh_token"); + if (isRefreshTokenRequest) + throw; // To prevent refresh token loop + var refresh_token = await storageService.GetItem("refresh_token"); if (refresh_token is null) throw; + var authManager = serviceProvider.GetRequiredService<AuthenticationManager>(); + // In the AuthenticationStateProvider, the access_token is refreshed using the refresh_token (if available). await authManager.RefreshToken(); - var access_token = await tokenProvider.GetAccessTokenAsync(); + var access_token = await tokenProvider.GetAccessToken(); if (string.IsNullOrEmpty(access_token)) throw; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs index c0f406f647..2ee7fec9cf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs @@ -1,10 +1,15 @@ -using System.Net; -using System.Text.Json; +//+:cnd:noEmit +using System.Net; namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class ExceptionDelegatingHandler(IStringLocalizer<AppStrings> localizer, JsonSerializerOptions jsonSerializerOptions, HttpClientHandler httpClientHandler) - : DelegatingHandler(httpClientHandler) +public partial class ExceptionDelegatingHandler(IStringLocalizer<AppStrings> localizer, + //#if (signalR != true) + PubSubService pubSubService, + //#endif + JsonSerializerOptions jsonSerializerOptions, + HttpMessageHandler handler) + : DelegatingHandler(handler) { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { @@ -56,5 +61,11 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage { throw new ServerConnectionException(localizer[nameof(AppStrings.ServerConnectionException)], exp); } + //#if (signalR != true) + finally + { + pubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, serverCommunicationSuccess); + } + //#endif } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs new file mode 100644 index 0000000000..8ed58f0f44 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs @@ -0,0 +1,20 @@ +namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; + +/// <summary> +/// Represents a delegate that creates a chain of HTTP message handlers. +/// +/// This delegate takes an underlying HTTP message handler, typically an <see cref="HttpClientHandler"/>, +/// and returns a chain of handlers that will process HTTP messages sequentially. +/// The returned chain will include the following handlers, in order: +/// +/// 1. <see cref="RequestHeadersDelegationHandler" /> +/// 2. <see cref="AuthDelegatingHandler" /> +/// 3. <see cref="RetryDelegatingHandler" /> +/// 4. <see cref="ExceptionDelegatingHandler" /> +/// +/// The chain is constructed in reverse order, with the provided `transportHandler` as the final +/// link. Each subsequent handler in the chain receives the output of the previous one. +/// </summary> +/// <param name="transportHandler">The underlying HTTP message handler to use for network communication.</param> +/// <returns>The constructed chain of HTTP message handlers.</returns> +public delegate HttpMessageHandler HttpMessageHandlersChainFactory(HttpMessageHandler transportHandler); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs index 48cff22e48..800333e55f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs @@ -3,7 +3,7 @@ namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class RequestHeadersDelegationHandler(AuthDelegatingHandler handler) +public partial class RequestHeadersDelegationHandler(HttpMessageHandler handler) : DelegatingHandler(handler) { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -11,7 +11,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request.SetBrowserRequestCredentials(BrowserRequestCredentials.Omit); request.SetBrowserResponseStreamingEnabled(true); - if (CultureInfoManager.MultilingualEnabled) + if (CultureInfoManager.MultilingualEnabled && string.IsNullOrEmpty(CultureInfo.CurrentUICulture.Name) is false) { request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentUICulture.Name)); } @@ -19,4 +19,3 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage return await base.SendAsync(request, cancellationToken); } } - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs index 1626265992..08d38b9ae8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs @@ -3,13 +3,13 @@ namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class RetryDelegatingHandler(ExceptionDelegatingHandler handler) +public partial class RetryDelegatingHandler(HttpMessageHandler handler) : DelegatingHandler(handler) { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - var delays = GetDelays(scaleFirstTry: TimeSpan.FromSeconds(3), maxRetries: 3).ToArray(); + var delays = GetDelaySequence(scaleFirstTry: TimeSpan.FromSeconds(3)).Take(3).ToArray(); Exception? lastExp = null; @@ -45,14 +45,14 @@ private static bool HasNoRetryPolicy(HttpRequestMessage request) return method.GetCustomAttribute<NoRetryPolicyAttribute>() is not null; } - private static IEnumerable<TimeSpan> GetDelays(TimeSpan scaleFirstTry, int maxRetries) + private static IEnumerable<TimeSpan> GetDelaySequence(TimeSpan scaleFirstTry) { TimeSpan maxValue = TimeSpan.MaxValue; var maxTimeSpanDouble = maxValue.Ticks - 1_000.0; var i = 0; var targetTicksFirstDelay = scaleFirstTry.Ticks; var num = 0.0; - for (; i < maxRetries; i++) + for (; i < int.MaxValue; i++) { var num2 = i + Random.Shared.NextDouble(); var next = Math.Pow(2.0, num2) * Math.Tanh(Math.Sqrt(4.0 * num2)); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/MessageBoxService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/MessageBoxService.cs index d6c5f6bd53..7f9add12eb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/MessageBoxService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/MessageBoxService.cs @@ -6,7 +6,7 @@ public partial class MessageBoxService private readonly ConcurrentQueue<MessageBoxData> queue = new(); - [AutoInject] private readonly IPubSubService pubSubService = default!; + [AutoInject] private readonly PubSubService pubSubService = default!; public Task<bool> Show(string message, string title = "") @@ -34,7 +34,7 @@ private async Task ProcessQueue() if (queue.TryDequeue(out var data)) { - pubSubService.Publish(PubSubMessages.SHOW_MESSAGE, data); + pubSubService.Publish(ClientPubSubMessages.SHOW_MESSAGE, data, persistent: true); await data.TaskCompletionSource.Task; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoOpPrerenderStateService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoOpPrerenderStateService.cs new file mode 100644 index 0000000000..5d94b5c1f7 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/NoOpPrerenderStateService.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; + +namespace Boilerplate.Client.Core.Services; + +/// <summary> +/// The Client.Core codebase is shared across different Blazor hosting models, such as Hybrid and WebAssembly, +/// which may or may not support pre-rendering. Additionally, the pre-rendering configuration can vary, +/// being either enabled or disabled. To ensure compatibility across all scenarios, regardless of pre-rendering state, +/// we introduce the IPrerenderStateService interface. This interface provides a GetValue method for data retrieval +/// (e.g., in UserMenu’s OnInitAsync method). +/// The WebPrerenderService implementation of IPrerenderStateService supports pre-rendering by leveraging +/// PersistentComponentState to persist data across renders. However, for scenarios like Blazor Hybrid, where +/// pre-rendering is not applicable, a 'Noop' (no operation) implementation is also provided. This Noop implementation +/// simply executes the passed function and returns the result without persisting any data to PersistentComponentState. +/// </summary> +public class NoopPrerenderStateService : IPrerenderStateService +{ + public ValueTask DisposeAsync() => ValueTask.CompletedTask; + + public Task<T?> GetValue<T>(Func<Task<T?>> factory, + [CallerLineNumber] int lineNumber = 0, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "") + { + return factory(); + } + + public Task<T?> GetValue<T>(string key, Func<Task<T?>> factory) + { + return factory(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubMessages.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubMessages.cs deleted file mode 100644 index 7104d05c18..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubMessages.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Boilerplate.Client.Core.Services; - -public static partial class PubSubMessages -{ - public const string USER_DATA_UPDATED = nameof(USER_DATA_UPDATED); - public const string SHOW_MESSAGE = nameof(SHOW_MESSAGE); - public const string CULTURE_CHANGED = nameof(CULTURE_CHANGED); -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubService.cs index f44bfec30a..62818332d8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PubSubService.cs @@ -1,34 +1,54 @@ namespace Boilerplate.Client.Core.Services; /// <summary> -/// For more information <see cref="IPubSubService"/> docs. +/// Service for Publish/Subscribe pattern. /// </summary> -public partial class PubSubService : IPubSubService +public partial class PubSubService { - [AutoInject] private IServiceProvider serviceProvider = default!; + private readonly ConcurrentDictionary<string, List<Func<object?, Task>>> handlers = []; - private readonly ConcurrentDictionary<string, List<Func<object?, Task>>> handlers = new(); + /// <summary> + /// Messages that were published before any handler was subscribed. + /// </summary> + private readonly ConcurrentBag<(string message, object? payload)> persistentMessages = []; - public void Publish(string message, object? payload) + [AutoInject] private readonly IServiceProvider serviceProvider = default!; + + public void Publish(string message, object? payload = null, bool persistent = false) { if (handlers.TryGetValue(message, out var messageHandlers)) { foreach (var handler in messageHandlers.ToArray()) { - handler(payload) - .ContinueWith(t => serviceProvider.GetRequiredService<IExceptionHandler>().Handle(t.Exception!), TaskContinuationOptions.OnlyOnFaulted); + handler(payload).ContinueWith(handleException, TaskContinuationOptions.OnlyOnFaulted); } } + else if (persistent) + { + persistentMessages.Add((message, payload)); + } } public Action Subscribe(string message, Func<object?, Task> handler) { - var messageHandlers = handlers.ContainsKey(message) - ? handlers[message] - : handlers[message] = []; + var messageHandlers = handlers.TryGetValue(message, out var value) ? value : handlers[message] = []; messageHandlers.Add(handler); + foreach (var (notHandledMessage, payload) in persistentMessages) + { + if (notHandledMessage == message) + { + handler(payload).ContinueWith(handleException, TaskContinuationOptions.OnlyOnFaulted); + persistentMessages.TryTake(out _); + } + } + return () => messageHandlers.Remove(handler); } + + private void handleException(Task t) + { + serviceProvider.GetRequiredService<IExceptionHandler>().Handle(t.Exception!); + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs new file mode 100644 index 0000000000..22fe69ae15 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs @@ -0,0 +1,39 @@ +using Boilerplate.Shared.Dtos.PushNotification; +using Boilerplate.Shared.Controllers.PushNotification; +using Microsoft.Extensions.Logging; + +namespace Boilerplate.Client.Core.Services; + +public abstract partial class PushNotificationServiceBase : IPushNotificationService +{ + [AutoInject] protected ILogger<PushNotificationServiceBase> Logger = default!; + [AutoInject] protected IPushNotificationController pushNotificationController = default!; + + public virtual string Token { get; set; } + public virtual Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken) => Task.FromResult(false); + public abstract Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken); + + public async Task RegisterDevice(CancellationToken cancellationToken) + { + if (await IsPushNotificationSupported(cancellationToken) is false) + { + Logger.LogWarning("Notifications are not supported/allowed on this platform/device."); + return; + } + + var deviceInstallation = await GetDeviceInstallation(cancellationToken); + + if (deviceInstallation is null) + { + Logger.LogWarning("Could not retrieve device installation"); // Browser's incognito mode etc. + return; + } + + await pushNotificationController.RegisterDevice(deviceInstallation, cancellationToken); + } + + public async Task DeregisterDevice(string deviceInstallationId, CancellationToken cancellationToken) + { + await pushNotificationController.DeregisterDevice(deviceInstallationId, cancellationToken); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SignalRInfinitiesRetryPolicy.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SignalRInfinitiesRetryPolicy.cs new file mode 100644 index 0000000000..99e519dd8f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SignalRInfinitiesRetryPolicy.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.SignalR.Client; + +namespace Boilerplate.Client.Core.Services; + +public class SignalRInfinitiesRetryPolicy : IRetryPolicy +{ + private static TimeSpan[] delays = new double[] { 1, 3, 5, 10, 15, 20, 30, 45, 59 } + .Select(TimeSpan.FromSeconds) + .ToArray(); + + public TimeSpan? NextRetryDelay(RetryContext retryContext) + { + var index = retryContext.PreviousRetryCount; + + if (index < delays.Length) + { + return delays[index]; + } + + return TimeSpan.FromMinutes(1); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SnackBarService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SnackBarService.cs new file mode 100644 index 0000000000..da330f1213 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SnackBarService.cs @@ -0,0 +1,15 @@ +namespace Boilerplate.Client.Core.Services; + +public partial class SnackBarService +{ + [AutoInject] private readonly PubSubService pubSubService = default!; + + + public void Show(string title, string body = "", BitColor color = BitColor.Info) + { + pubSubService.Publish(ClientPubSubMessages.SHOW_SNACK, (title, body, color), persistent: true); + } + + public void Error(string title, string body = "") => Show(title, body, BitColor.Error); + public void Success(string title, string body = "") => Show(title, body, BitColor.Success); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ThemeService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ThemeService.cs new file mode 100644 index 0000000000..f03bbe9e29 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ThemeService.cs @@ -0,0 +1,37 @@ +namespace Boilerplate.Client.Core.Services; + +public partial class ThemeService +{ + [AutoInject] private Cookie cookie = default!; + [AutoInject] private PubSubService pubSubService = default!; + [AutoInject] private BitThemeManager bitThemeManager = default!; + [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; + + + public async Task<AppThemeType> GetCurrentTheme() + { + var theme = await bitThemeManager.GetCurrentThemeAsync(); + return theme == "dark" ? AppThemeType.Dark : AppThemeType.Light; + } + + public async Task<AppThemeType> ToggleTheme() + { + var newThemeName = await bitThemeManager.ToggleDarkLightAsync(); + + var isDark = newThemeName == "dark"; + await bitDeviceCoordinator.ApplyTheme(isDark); + + var theme = isDark ? AppThemeType.Dark : AppThemeType.Light; + pubSubService.Publish(ClientPubSubMessages.THEME_CHANGED, theme); + + await cookie.Set(new() + { + MaxAge = 30 * 24 * 3600, + Name = "bit.Theme", + Secure = AppEnvironment.IsDev() is false, + Value = Uri.EscapeDataString(newThemeName), + }); + + return theme; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/ThemeColors.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/ThemeColors.cs index b072e2705b..26328f40f3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/ThemeColors.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/ThemeColors.cs @@ -2,7 +2,7 @@ public partial class ThemeColors { - public static readonly string PrimaryDarkBgColor = "#000000"; + public static readonly string PrimaryDarkBgColor = "#010409"; public static readonly string PrimaryLightBgColor = "#FFFFFF"; // In case you need to change the background color, make sure to also update app.scss's --bit-clr-bg-pri accordingly. } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_bit-css-variables.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_bit-css-variables.scss index 9a08fda500..3182fe1539 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_bit-css-variables.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_bit-css-variables.scss @@ -102,6 +102,9 @@ $bit-color-foreground-primary-active: var(--bit-clr-fg-pri-active); $bit-color-foreground-secondary: var(--bit-clr-fg-sec); $bit-color-foreground-secondary-hover: var(--bit-clr-fg-sec-hover); $bit-color-foreground-secondary-active: var(--bit-clr-fg-sec-hover); +$bit-color-foreground-tertiary: var(--bit-clr-fg-ter); +$bit-color-foreground-tertiary-hover: var(--bit-clr-fg-ter-hover); +$bit-color-foreground-tertiary-active: var(--bit-clr-fg-ter-hover); $bit-color-foreground-disabled: var(--bit-clr-fg-dis); //backgrounds @@ -111,6 +114,9 @@ $bit-color-background-primary-active: var(--bit-clr-bg-pri-active); $bit-color-background-secondary: var(--bit-clr-bg-sec); $bit-color-background-secondary-hover: var(--bit-clr-bg-sec-hover); $bit-color-background-secondary-active: var(--bit-clr-bg-sec-active); +$bit-color-background-tertiary: var(--bit-clr-bg-ter); +$bit-color-background-tertiary-hover: var(--bit-clr-bg-ter-hover); +$bit-color-background-tertiary-active: var(--bit-clr-bg-ter-active); $bit-color-background-disabled: var(--bit-clr-bg-dis); $bit-color-background-overlay: var(--bit-clr-bg-overlay); @@ -121,6 +127,9 @@ $bit-color-border-primary-active: var(--bit-clr-brd-pri-active); $bit-color-border-secondary: var(--bit-clr-brd-sec); $bit-color-border-secondary-hover: var(--bit-clr-brd-sec-hover); $bit-color-border-secondary-active: var(--bit-clr-brd-sec-active); +$bit-color-border-tertiary: var(--bit-clr-brd-ter); +$bit-color-border-tertiary-hover: var(--bit-clr-brd-ter-hover); +$bit-color-border-tertiary-active: var(--bit-clr-brd-ter-active); $bit-color-border-disabled: var(--bit-clr-brd-dis); //required @@ -180,3 +189,134 @@ $bit-box-shadow-21: var(--bit-shd-21); $bit-box-shadow-22: var(--bit-shd-22); $bit-box-shadow-23: var(--bit-shd-23); $bit-box-shadow-24: var(--bit-shd-24); + +//spacing +$bit-spacing-scaling-factor: var(--bit-spa-scaling-factor); + +//z-index +$bit-zindex-snackbar: var(--bit-zin-snackbar); +$bit-zindex-modal: var(--bit-zin-modal); +$bit-zindex-callout: var(--bit-zin-callout); +$bit-zindex-overlay: var(--bit-zin-overlay); +$bit-zindex-base: var(--bit-zin-base); + +//shape +$bit-shape-border-radius: var(--bit-shp-brd-radius); +$bit-shape-border-width: var(--bit-shp-brd-width); +$bit-shape-border-style: var(--bit-shp-brd-style); + +/*---- Typography ----*/ +$bit-typography-font-family: var(--bit-tpg-font-family); +$bit-typography-font-weight: var(--bit-tpg-font-weight); +$bit-typography-line-height: var(--bit-tpg-line-height); +$bit-typography-gutter-size: var(--bit-tpg-gutter-size); + +// h1 +$bit-typography-h1-margin: var(--bit-tpg-h1-margin); +$bit-typography-h1-font-weight: var(--bit-tpg-h1-font-weight); +$bit-typography-h1-font-size: var(--bit-tpg-h1-font-size); +$bit-typography-h1-line-height: var(--bit-tpg-h1-line-height); +$bit-typography-h1-letter-spacing: var(--bit-tpg-h1-letter-spacing); + +// h2 +$bit-typography-h2-margin: var(--bit-tpg-h2-margin); +$bit-typography-h2-font-weight: var(--bit-tpg-h2-font-weight); +$bit-typography-h2-font-size: var(--bit-tpg-h2-font-size); +$bit-typography-h2-line-height: var(--bit-tpg-h2-line-height); +$bit-typography-h2-letter-spacing: var(--bit-tpg-h2-letter-spacing); + +// h3 +$bit-typography-h3-margin: var(--bit-tpg-h3-margin); +$bit-typography-h3-font-weight: var(--bit-tpg-h3-font-weight); +$bit-typography-h3-font-size: var(--bit-tpg-h3-font-size); +$bit-typography-h3-line-height: var(--bit-tpg-h3-line-height); +$bit-typography-h3-letter-spacing: var(--bit-tpg-h3-letter-spacing); + +// h4 +$bit-typography-h4-margin: var(--bit-tpg-h4-margin); +$bit-typography-h4-font-weight: var(--bit-tpg-h4-font-weight); +$bit-typography-h4-font-size: var(--bit-tpg-h4-font-size); +$bit-typography-h4-line-height: var(--bit-tpg-h4-line-height); +$bit-typography-h4-letter-spacing: var(--bit-tpg-h4-letter-spacing); + +// h5 +$bit-typography-h5-margin: var(--bit-tpg-h5-margin); +$bit-typography-h5-font-weight: var(--bit-tpg-h5-font-weight); +$bit-typography-h5-font-size: var(--bit-tpg-h5-font-size); +$bit-typography-h5-line-height: var(--bit-tpg-h5-line-height); +$bit-typography-h5-letter-spacing: var(--bit-tpg-h5-letter-spacing); + +// h6 +$bit-typography-h6-margin: var(--bit-tpg-h6-margin); +$bit-typography-h6-font-weight: var(--bit-tpg-h6-font-weight); +$bit-typography-h6-font-size: var(--bit-tpg-h6-font-size); +$bit-typography-h6-line-height: var(--bit-tpg-h6-line-height); +$bit-typography-h6-letter-spacing: var(--bit-tpg-h6-letter-spacing); + +// subtitle1 +$bit-typography-subtitle1-margin: var(--bit-tpg-subtitle1-margin); +$bit-typography-subtitle1-font-weight: var(--bit-tpg-subtitle1-font-weight); +$bit-typography-subtitle1-font-size: var(--bit-tpg-subtitle1-font-size); +$bit-typography-subtitle1-line-height: var(--bit-tpg-subtitle1-line-height); +$bit-typography-subtitle1-letter-spacing: var(--bit-tpg-subtitle1-letter-spacing); + +// subtitle2 +$bit-typography-subtitle2-margin: var(--bit-tpg-subtitle2-margin); +$bit-typography-subtitle2-font-weight: var(--bit-tpg-subtitle2-font-weight); +$bit-typography-subtitle2-font-size: var(--bit-tpg-subtitle2-font-size); +$bit-typography-subtitle2-line-height: var(--bit-tpg-subtitle2-line-height); +$bit-typography-subtitle2-letter-spacing: var(--bit-tpg-subtitle2-letter-spacing); + +// body1 +$bit-typography-body1-margin: var(--bit-tpg-body1-margin); +$bit-typography-body1-font-weight: var(--bit-tpg-body1-font-weight); +$bit-typography-body1-font-size: var(--bit-tpg-body1-font-size); +$bit-typography-body1-line-height: var(--bit-tpg-body1-line-height); +$bit-typography-body1-letter-spacing: var(--bit-tpg-body1-letter-spacing); + +// body2 +$bit-typography-body2-margin: var(--bit-tpg-body2-margin); +$bit-typography-body2-font-weight: var(--bit-tpg-body2-font-weight); +$bit-typography-body2-font-size: var(--bit-tpg-body2-font-size); +$bit-typography-body2-line-height: var(--bit-tpg-body2-line-height); +$bit-typography-body2-letter-spacing: var(--bit-tpg-body2-letter-spacing); + +// button +$bit-typography-button-margin: var(--bit-tpg-button-margin); +$bit-typography-button-font-weight: var(--bit-tpg-button-font-weight); +$bit-typography-button-font-size: var(--bit-tpg-button-font-size); +$bit-typography-button-line-height: var(--bit-tpg-button-line-height); +$bit-typography-button-letter-spacing: var(--bit-tpg-button-letter-spacing); +$bit-typography-button-text-transform: var(--bit-tpg-button-text-transform); +$bit-typography-button-display: var(--bit-tpg-button-display); + +// caption1 +$bit-typography-caption1-margin: var(--bit-tpg-caption1-margin); +$bit-typography-caption1-font-weight: var(--bit-tpg-caption1-font-weight); +$bit-typography-caption1-font-size: var(--bit-tpg-caption1-font-size); +$bit-typography-caption1-line-height: var(--bit-tpg-caption1-line-height); +$bit-typography-caption1-letter-spacing: var(--bit-tpg-caption1-letter-spacing); + +// caption2 +$bit-typography-caption2-margin: var(--bit-tpg-caption2-margin); +$bit-typography-caption2-font-weight: var(--bit-tpg-caption2-font-weight); +$bit-typography-caption2-font-size: var(--bit-tpg-caption2-font-size); +$bit-typography-caption2-line-height: var(--bit-tpg-caption2-line-height); +$bit-typography-caption2-letter-spacing: var(--bit-tpg-caption2-letter-spacing); + +// overline +$bit-typography-overline-margin: var(--bit-tpg-overline-margin); +$bit-typography-overline-font-weight: var(--bit-tpg-overline-font-weight); +$bit-typography-overline-font-size: var(--bit-tpg-overline-font-size); +$bit-typography-overline-line-height: var(--bit-tpg-overline-line-height); +$bit-typography-overline-letter-spacing: var(--bit-tpg-overline-letter-spacing); +$bit-typography-overline-text-transform: var(--bit-tpg-overline-text-transform); +$bit-typography-overline-display: var(--bit-tpg-overline-display); + +// inherit +$bit-typography-inherit-margin: var(--bit-tpg-inherit-margin); +$bit-typography-inherit-font-family: var(--bit-tpg-inherit-font-family); +$bit-typography-inherit-font-weight: var(--bit-tpg-inherit-font-weight); +$bit-typography-inherit-font-size: var(--bit-tpg-inherit-font-size); +$bit-typography-inherit-line-height: var(--bit-tpg-inherit-line-height); +$bit-typography-inherit-letter-spacing: var(--bit-tpg-inherit-letter-spacing); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_colors.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_colors.scss deleted file mode 100644 index 194cabaf34..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_colors.scss +++ /dev/null @@ -1,71 +0,0 @@ -$PrimaryColor: #0078D4; -$SecondaryColor: #002A66; -$S1Color: #183977; -$S2Color: #204383; -$S3Color: #7186B4; -$S4Color: #C1CADF; - -$Black: #201F1E; -$Black3: #323130; -$Red: #A4262C; -$Green: #27cb3c; -$Blue: #0078D4; -$Purple: #b200ff; -$Pink: #ff00dc; -$Orange: #ff6a00; -$Yellow: #ffd800; - -$Gray1: #8A8886; -$Gray2: #A19F9D; -$Gray3: #DDDDDD; -$Gray4: #EDEBE9; -$Gray5: #F2F2F2; -$Gray6: #FCFCFC; -/////////////////////////////////////// -$Blue1: #002A66; -$Blue2: #003681; -$BackgroundColor: #FBFCFF; - -$TypePrimaryColor: #323130; -$TypeSecondaryColor: #605E5C; -$TypeDisabledColor: #A19F9D; - -$BodyDividerColor: #EDEBE9; -$InputBorderColor: #8A8886; -$InputBorderHoverColor: #323130; - -$ThemePrimaryColor: #0078D4; -$ThemeLighterAltColor: #EFF6FC; -$ThemeLighterColor: #DEECF9; -$ThemeLightColor: #C7E0F4; -$ThemeTertiaryColor: #2B88D8; -$ThemeDarkerAltColor: #106EBE; -$ThemeDarkerColor: #004578; -$ThemeDarkColor: #005A9E; - -$ErrorBackgroundFillColor: #FDE7E9; -$SuccessBackgroundFillColor: #DFF6DD; -$SevereWarningBackgroundFillColor: #FED9CC; -$WarningBackgroundFillColor: #FFF4CE; - -$StatusErrorColor: #A80000; -$StatusSuccessColor: #107C10; -$StatusSevereWarningColor: #D83B01; -$StatusWarningColor: #797775; - -$OverlayLightColor: rgba(255, 255, 255, 0.4); -$OverlayDarkColor: rgba(0, 0, 0, 0.4); - -$White: #FFFFFF; -$Gray10: #FAF9F8; -$Gray20: #F3F2F1; -$Gray30: #EDEBE9; -$Gray40: #E1DFDD; -$Gray50: #D2D0CE; -$Gray60: #C8C6C4; -$Gray90: #A19F9D; -$Gray110: #8A8886; -$Gray130: #605E5C; -$Gray150: #3B3A39; -$Gray160: #323130; -$Gray190: #201F1E; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_functions.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_functions.scss deleted file mode 100644 index c2359935dd..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_functions.scss +++ /dev/null @@ -1,15 +0,0 @@ -@use 'sass:math'; - -$html-font-size: 16px; - -@function stripUnit($value) { - @return math.div($value, ($value * 0 + 1)); -} - -@function em2($pxValue, $base-font-size: $html-font-size) { - @return #{calc(stripUnit($pxValue) / stripUnit($base-font-size))}em; -} - -@function rem2($pxValue) { - @return #{calc(stripUnit($pxValue) / stripUnit($html-font-size))}rem; -} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss index 7abd9a8404..5f59c7cbd7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss @@ -1,22 +1,26 @@ -@import '_functions.scss'; - -/// https://github.com/Necromancerx/media-queries-scss-mixins +/// https://github.com/Necromancerx/media-queries-scss-mixins // media aliases and breakpoints -$screen-xl-min: rem2(1920px); -$screen-xl-max: rem2(5000px); - -$screen-lg-min: rem2(1280px); -$screen-lg-max: rem2(1919px); - -$screen-md-min: rem2(769px); -$screen-md-max: rem2(1279px); +$screen-sm-min: 600px; +$screen-md-min: 960px; +$screen-lg-min: 1280px; +$screen-xl-min: 1920px; -$screen-sm-max: rem2(768px); +$screen-xs-max: 599px; +$screen-sm-max: 959px; +$screen-md-max: 1279px; +$screen-lg-max: 1919px; +$screen-xl-max: 5000px; // media devices +@mixin xs { + @media screen and (max-width: #{$screen-xs-max}) { + @content; + } +} + @mixin sm { - @media screen and (max-width: #{$screen-sm-max}) { + @media screen and (min-width: #{$screen-sm-min}) and (max-width: #{$screen-sm-max}) { @content; } } @@ -40,6 +44,12 @@ $screen-sm-max: rem2(768px); } // media lt queries +@mixin lt-sm { + @media screen and (max-width: #{$screen-xs-max}) { + @content; + } +} + @mixin lt-md { @media screen and (max-width: #{$screen-sm-max}) { @content; @@ -59,6 +69,12 @@ $screen-sm-max: rem2(768px); } // media gt queries +@mixin gt-xs { + @media screen and (min-width: #{$screen-sm-min}) { + @content; + } +} + @mixin gt-sm { @media screen and (min-width: #{$screen-md-min}) { @content; @@ -75,4 +91,4 @@ $screen-sm-max: rem2(768px); @media screen and (min-width: #{$screen-xl-min}) { @content; } -} +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_vars.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_vars.scss deleted file mode 100644 index e897a9f119..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/abstracts/_vars.scss +++ /dev/null @@ -1,3 +0,0 @@ -$headerHeight: 48px; -$footerHeight: 68px; -$navMenuWidth: 256px; \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/app.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/app.scss index eb84cab246..b850fa2043 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/app.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Styles/app.scss @@ -1,12 +1,32 @@ -:root { - --bit-status-bar-height: 0px; -} +@import '../Styles/abstracts/_media-queries.scss'; :root[bit-theme="dark"] { - --bit-clr-bg-pri: #000000; + //--bit-clr-bg-pri: #010409; // In case you need to change the background color, make sure to also update ThemeColors.cs's PrimaryDarkBgColor accordingly. } +:root { + --app-inset-top: env(safe-area-inset-top); + --app-inset-left: env(safe-area-inset-left); + --app-inset-right: env(safe-area-inset-right); + --app-inset-bottom: env(safe-area-inset-bottom); + //-- + --app-width-vw: calc(100vw - var(--app-inset-left) - var(--app-inset-right)); + --app-height-vh: calc(100vh - var(--app-inset-top) - var(--app-inset-bottom)); + --app-width-per: calc(100% - var(--app-inset-left) - var(--app-inset-right)); + --app-height-per: calc(100% - var(--app-inset-top) - var(--app-inset-bottom)); + --app-width: calc(var(--win-width) - var(--app-inset-left) - var(--app-inset-right)); + --app-height: calc(var(--win-height) - var(--app-inset-top) - var(--app-inset-bottom)); + //-- + --app-inset-inline-start: var(--app-inset-left); + --app-inset-inline-end: var(--app-inset-right); + + [dir="rtl"] { + --app-inset-inline-start: var(--app-inset-right); + --app-inset-inline-end: var(--app-inset-left); + } +} + * { box-sizing: border-box; font-family: "Segoe UI"; @@ -20,7 +40,12 @@ html, body, #app-container { padding: 0; width: 100%; height: 100%; + overflow: auto; scroll-behavior: smooth; + + @include lt-md { + overflow: hidden; + } } h1, h2, h3, h4, h5 { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Development.json index 7a73a41bfd..2b7a979354 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Development.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Development.json @@ -1,2 +1,3 @@ { + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Production.json index 544b7b4ddd..2b7a979354 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Production.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.Production.json @@ -1,3 +1,3 @@ { - + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json index 223ca44e6d..c78c02f339 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json @@ -12,10 +12,6 @@ */ //#endif "ServerAddress_Comment": "If you're running Boilerplate.Server.Web project, then you can also use relative urls such as / for Blazor Server and WebAssembly", - "WindowsUpdateSettings": { - "FilesUrl": null, - "AutoReload": true - }, //#if (captcha == "reCaptcha") "GoogleRecaptchaSiteKey": "6LdMKr4pAAAAAKMyuEPn3IHNf04EtULXA8uTIVRw", //#endif diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/compilerconfig.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/compilerconfig.json index bdd69eb4fe..b836243a4e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/compilerconfig.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/compilerconfig.json @@ -13,44 +13,62 @@ "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/ConfirmMessageBox.razor.css", - "inputFile": "Components/Layout/ConfirmMessageBox.razor.scss", + "outputFile": "Components/Layout/AuthorizedHeader.razor.css", + "inputFile": "Components/Layout/AuthorizedHeader.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/Footer.razor.css", - "inputFile": "Components/Layout/Footer.razor.scss", + "outputFile": "Components/Layout/DiagnosticModal.razor.css", + "inputFile": "Components/Layout/DiagnosticModal.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/Header.razor.css", - "inputFile": "Components/Layout/Header.razor.scss", + "outputFile": "Components/Layout/IdentityHeader.razor.css", + "inputFile": "Components/Layout/IdentityHeader.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/MainLayout.razor.css", - "inputFile": "Components/Layout/MainLayout.razor.scss", + "outputFile": "Components/Layout/MessageBox.razor.css", + "inputFile": "Components/Layout/MessageBox.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/MessageBox.razor.css", - "inputFile": "Components/Layout/MessageBox.razor.scss", + "outputFile": "Components/Layout/NavBar.razor.css", + "inputFile": "Components/Layout/NavBar.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Layout/NavPanel.razor.css", + "inputFile": "Components/Layout/NavPanel.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Layout/RootContainer.razor.css", + "inputFile": "Components/Layout/RootContainer.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Layout/RootLayout.razor.css", + "inputFile": "Components/Layout/RootLayout.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/NavMenu.razor.css", - "inputFile": "Components/Layout/NavMenu.razor.scss", + "outputFile": "Components/Layout/SignOutConfirmDialog.razor.css", + "inputFile": "Components/Layout/SignOutConfirmDialog.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Layout/SignOutConfirmModal.razor.css", - "inputFile": "Components/Layout/SignOutConfirmModal.razor.scss", + "outputFile": "Components/Layout/UserMenu.razor.css", + "inputFile": "Components/Layout/UserMenu.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, @@ -78,45 +96,40 @@ "minify": { "enabled": false }, "options": { "sourceMap": false } }, + // identity { - "outputFile": "Components/Pages/Identity/Profile/ProfilePage.razor.css", - "inputFile": "Components/Pages/Identity/Profile/ProfilePage.razor.scss", - "minify": { "enabled": false }, - "options": { "sourceMap": false } - }, - { - "outputFile": "Components/Pages/Identity/Profile/UserDataSection.razor.css", - "inputFile": "Components/Pages/Identity/Profile/UserDataSection.razor.scss", + "outputFile": "Components/Pages/Identity/Components/SocialRow.razor.css", + "inputFile": "Components/Pages/Identity/Components/SocialRow.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/Profile/TwoFactorSection.razor.css", - "inputFile": "Components/Pages/Identity/Profile/TwoFactorSection.razor.scss", + "outputFile": "Components/Pages/Identity/SignIn/OtpPanel.razor.css", + "inputFile": "Components/Pages/Identity/SignIn/OtpPanel.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/Profile/DeleteAccountSection.razor.css", - "inputFile": "Components/Pages/Identity/Profile/DeleteAccountSection.razor.scss", + "outputFile": "Components/Pages/Identity/SignIn/SignInPage.razor.css", + "inputFile": "Components/Pages/Identity/SignIn/SignInPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.css", - "inputFile": "Components/Pages/Identity/Profile/DeleteAccountConfirmModal.razor.scss", + "outputFile": "Components/Pages/Identity/SignIn/SignInPanel.razor.css", + "inputFile": "Components/Pages/Identity/SignIn/SignInPanel.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/Profile/ChangeEmailSection.razor.css", - "inputFile": "Components/Pages/Identity/Profile/ChangeEmailSection.razor.scss", + "outputFile": "Components/Pages/Identity/SignIn/TfaPanel.razor.css", + "inputFile": "Components/Pages/Identity/SignIn/TfaPanel.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.css", - "inputFile": "Components/Pages/Identity/Profile/ChangePhoneNumberSection.razor.scss", + "outputFile": "Components/Pages/Identity/SignUp/SignUpPage.razor.css", + "inputFile": "Components/Pages/Identity/SignUp/SignUpPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, @@ -138,87 +151,118 @@ "minify": { "enabled": false }, "options": { "sourceMap": false } }, + // authorized + { + "outputFile": "Components/Pages/Authorized/Settings/ChangeEmailSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/ChangeEmailSection.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/ChangePhoneNumberSection.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Pages/Authorized/Settings/DeleteAccountSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/DeleteAccountSection.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Pages/Authorized/Settings/ProfileSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/ProfileSection.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Components/Pages/Authorized/Settings/SessionsSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/SessionsSection.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, { - "outputFile": "Components/Pages/Identity/SignInPage.razor.css", - "inputFile": "Components/Pages/Identity/SignInPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Settings/SettingsPage.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/SettingsPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Identity/SignUpPage.razor.css", - "inputFile": "Components/Pages/Identity/SignUpPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Settings/TwoFactorSection.razor.css", + "inputFile": "Components/Pages/Authorized/Settings/TwoFactorSection.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, //#if (offlineDb == true) { - "outputFile": "Components/Pages/Offline/OfflineEditProfilePage.razor.css", - "inputFile": "Components/Pages/Offline/OfflineEditProfilePage.razor.scss", + "outputFile": "Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.css", + "inputFile": "Components/Pages/Authorized/Offline/OfflineEditProfilePage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, //#endif //#if (sample == "Todo") { - "outputFile": "Components/Pages/Todo/TodoPage.razor.css", - "inputFile": "Components/Pages/Todo/TodoPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Todo/TodoPage.razor.css", + "inputFile": "Components/Pages/Authorized/Todo/TodoPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, //#elif (sample == "Admin") { - "outputFile": "Components/Pages/Categories/AddOrEditCategoryPage.razor.css", - "inputFile": "Components/Pages/Categories/AddOrEditCategoryPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.css", + "inputFile": "Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Categories/CategoriesPage.razor.css", - "inputFile": "Components/Pages/Categories/CategoriesPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Categories/CategoriesPage.razor.css", + "inputFile": "Components/Pages/Authorized/Categories/CategoriesPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Dashboard/DashboardPage.razor.css", - "inputFile": "Components/Pages/Dashboard/DashboardPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Dashboard/DashboardPage.razor.css", + "inputFile": "Components/Pages/Authorized/Dashboard/DashboardPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Dashboard/OverallStatsWidget.razor.css", - "inputFile": "Components/Pages/Dashboard/OverallStatsWidget.razor.scss", + "outputFile": "Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.css", + "inputFile": "Components/Pages/Authorized/Dashboard/OverallStatsWidget.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.css", - "inputFile": "Components/Pages/Dashboard/ProductsCountPerCategoryWidget.razor.scss", + "outputFile": "Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.css", + "inputFile": "Components/Pages/Authorized/Dashboard/OverallStatusCard.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Dashboard/ProductsPercentageWidget.razor.css", - "inputFile": "Components/Pages/Dashboard/ProductsPercentageWidget.razor.scss", + "outputFile": "Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.css", + "inputFile": "Components/Pages/Authorized/Dashboard/ProductsCountPerCategoryWidget.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Dashboard/ProductsSalesWidget.razor.css", - "inputFile": "Components/Pages/Dashboard/ProductsSalesWidget.razor.scss", + "outputFile": "Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.css", + "inputFile": "Components/Pages/Authorized/Dashboard/ProductsPercentageWidget.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Products/AddOrEditProductModal.razor.css", - "inputFile": "Components/Pages/Products/AddOrEditProductModal.razor.scss", + "outputFile": "Components/Pages/Authorized/Products/AddOrEditProductModal.razor.css", + "inputFile": "Components/Pages/Authorized/Products/AddOrEditProductModal.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Components/Pages/Products/ProductsPage.razor.css", - "inputFile": "Components/Pages/Products/ProductsPage.razor.scss", + "outputFile": "Components/Pages/Authorized/Products/ProductsPage.razor.css", + "inputFile": "Components/Pages/Authorized/Products/ProductsPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } } //#endif -] \ No newline at end of file +] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json index 9882e9368d..770ab6d2b3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/403.svg b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/403.svg new file mode 100644 index 0000000000..efac3f04d6 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/403.svg @@ -0,0 +1,18 @@ +<svg width="587" height="553" viewBox="0 0 587 553" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M55.93 172.626H177.331L167.132 274.945H188.188L182.266 321.992H162.197L157.262 372H100.016L95.081 321.992H6.58L9.212 274.945L55.93 172.626ZM84.882 220.002L59.22 274.945H90.146L84.882 220.002ZM321.86 225.266C318.789 215.177 312.648 210.132 303.436 210.132C294.224 210.132 287.973 215.177 284.683 225.266C281.612 235.355 280.077 250.928 280.077 271.984C280.077 312.561 287.863 332.849 303.436 332.849C319.008 332.849 326.795 312.561 326.795 271.984C326.795 250.928 325.15 235.355 321.86 225.266ZM375.158 347.983C355.418 366.188 331.51 375.29 303.436 375.29C275.361 375.29 251.454 366.188 231.714 347.983C211.974 329.559 202.104 304.445 202.104 272.642C202.104 240.839 211.206 215.506 229.411 196.643C247.615 177.561 272.29 168.02 303.436 168.02C334.581 168.02 359.256 177.561 377.461 196.643C395.665 215.506 404.768 240.839 404.768 272.642C404.768 304.445 394.898 329.559 375.158 347.983ZM495.489 299.62C495.489 278.783 479.368 268.365 447.126 268.365H441.204L469.169 218.028L421.464 209.145L431.992 172.626H571.817L534.311 237.11C535.408 237.549 536.943 238.207 538.917 239.084C540.891 239.742 544.291 241.716 549.116 245.006C553.942 248.077 558.219 251.805 561.947 256.192C565.676 260.359 569.076 266.281 572.146 273.958C575.217 281.415 576.752 289.64 576.752 298.633C576.752 320.566 568.198 338.881 551.09 353.576C533.982 368.052 513.365 375.29 489.238 375.29C476.956 375.29 464.892 373.316 453.048 369.368C441.424 365.201 432.87 361.033 427.386 356.866L418.832 350.944L434.624 319.36C445.152 325.282 455.242 328.243 464.892 328.243C474.543 328.243 482 325.83 487.264 321.005C492.748 316.18 495.489 309.051 495.489 299.62Z" fill="#8B9199"/> +<path d="M420.839 279.27L453.493 257.889" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M116.931 469.551L508.176 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M536.168 469.551L557 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M31 469.551L80.4752 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M370.389 248.992L387.435 213.938" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M294.65 242.95L295.497 204" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M219.973 249.08L201.626 214.686" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M169.593 279.164L137.389 257.115" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<rect x="245.175" y="307.234" width="182.277" height="162.317" fill="#DECC78"/> +<path d="M245.175 307.234H427.452L466.837 365.668H283.258L245.175 307.234Z" fill="#F6E387"/> +<rect x="161.849" y="307.234" width="83.3267" height="162.317" fill="#C0AF67"/> +<path d="M161.848 307.234H245.175L206.116 365.668H122.789L161.848 307.234Z" fill="#DECC78"/> +<ellipse cx="399.46" cy="388.393" rx="9.11386" ry="9.08977" fill="#343A41"/> +<ellipse cx="274.469" cy="388.393" rx="9.11386" ry="9.08977" fill="#343A41"/> +<path d="M280.447 456.241C278.944 456.241 277.718 455.021 277.788 453.519C278.466 438.926 284.577 425.068 294.98 414.692C306.029 403.673 321.014 397.482 336.639 397.482C352.264 397.482 367.249 403.673 378.298 414.692C388.702 425.068 394.813 438.926 395.491 453.519C395.56 455.021 394.335 456.241 392.831 456.241C391.327 456.241 390.116 455.021 390.039 453.519C389.365 440.367 383.83 427.89 374.448 418.533C364.42 408.532 350.82 402.913 336.639 402.913C322.458 402.913 308.858 408.532 298.831 418.533C289.448 427.89 283.913 440.367 283.24 453.519C283.163 455.021 281.951 456.241 280.447 456.241Z" fill="#343A41"/> +</svg> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/404.svg b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/404.svg new file mode 100644 index 0000000000..c9cb6dc997 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/404.svg @@ -0,0 +1,18 @@ +<svg width="607" height="553" viewBox="0 0 607 553" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M55.93 172.626H177.331L167.132 274.945H188.188L182.266 321.992H162.197L157.262 372H100.016L95.081 321.992H6.58L9.212 274.945L55.93 172.626ZM84.882 220.002L59.22 274.945H90.146L84.882 220.002ZM321.86 225.266C318.789 215.177 312.648 210.132 303.436 210.132C294.224 210.132 287.973 215.177 284.683 225.266C281.612 235.355 280.077 250.928 280.077 271.984C280.077 312.561 287.863 332.849 303.436 332.849C319.008 332.849 326.795 312.561 326.795 271.984C326.795 250.928 325.15 235.355 321.86 225.266ZM375.158 347.983C355.418 366.188 331.51 375.29 303.436 375.29C275.361 375.29 251.454 366.188 231.714 347.983C211.974 329.559 202.104 304.445 202.104 272.642C202.104 240.839 211.206 215.506 229.411 196.643C247.615 177.561 272.29 168.02 303.436 168.02C334.581 168.02 359.256 177.561 377.461 196.643C395.665 215.506 404.768 240.839 404.768 272.642C404.768 304.445 394.898 329.559 375.158 347.983ZM468.182 172.626H589.583L579.384 274.945H600.44L594.518 321.992H574.449L569.514 372H512.268L507.333 321.992H418.832L421.464 274.945L468.182 172.626ZM497.134 220.002L471.472 274.945H502.398L497.134 220.002Z" fill="#8B9199"/> +<path d="M429.839 279.27L462.493 257.889" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M125.931 469.551L517.176 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M545.168 469.551L566 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M40 469.551L89.4752 469.551" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M379.389 248.992L396.435 213.938" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M303.65 242.95L304.497 204" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M228.973 249.08L210.626 214.686" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<path d="M178.593 279.164L146.389 257.115" stroke="#457FCC" stroke-width="6" stroke-linecap="round"/> +<rect x="254.175" y="307.234" width="182.277" height="162.317" fill="#DECC78"/> +<path d="M254.175 307.234H436.452L475.837 365.668H292.258L254.175 307.234Z" fill="#F6E387"/> +<rect x="170.849" y="307.234" width="83.3267" height="162.317" fill="#C0AF67"/> +<path d="M170.848 307.234H254.175L215.116 365.668H131.789L170.848 307.234Z" fill="#DECC78"/> +<ellipse cx="408.46" cy="388.393" rx="9.11386" ry="9.08977" fill="#343A41"/> +<ellipse cx="283.469" cy="388.393" rx="9.11386" ry="9.08977" fill="#343A41"/> +<path d="M289.447 456.241C287.944 456.241 286.718 455.021 286.788 453.519C287.466 438.926 293.577 425.069 303.98 414.692C315.029 403.673 330.014 397.482 345.639 397.482C361.264 397.482 376.249 403.673 387.298 414.692C397.702 425.069 403.813 438.926 404.491 453.519C404.56 455.021 403.335 456.241 401.831 456.241C400.327 456.241 399.116 455.021 399.039 453.519C398.365 440.367 392.83 427.89 383.448 418.533C373.42 408.532 359.82 402.913 345.639 402.913C331.458 402.913 317.858 408.532 307.831 418.533C298.448 427.89 292.913 440.367 292.24 453.519C292.163 455.021 290.951 456.241 289.447 456.241Z" fill="#343A41"/> +</svg> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-GB.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-GB.webp new file mode 100644 index 0000000000..7f9582c698 Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-GB.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-US.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-US.webp new file mode 100644 index 0000000000..6b67a22a9c Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/en-US.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fa-IR.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fa-IR.webp new file mode 100644 index 0000000000..1ad31cbe99 Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fa-IR.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fr-FR.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fr-FR.webp new file mode 100644 index 0000000000..1597a3a39b Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/fr-FR.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/nl-NL.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/nl-NL.webp new file mode 100644 index 0000000000..61ec733cc4 Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/flags/nl-NL.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/gmail-icon.png b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/gmail-icon.png new file mode 100644 index 0000000000..b68b47c09b Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/gmail-icon.png differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/outlook-icon.png b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/outlook-icon.png new file mode 100644 index 0000000000..7d2e2a6eff Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/icons/outlook-icon.png differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/identitylayout-image.webp b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/identitylayout-image.webp new file mode 100644 index 0000000000..af30521622 Binary files /dev/null and b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/identitylayout-image.webp differ diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs index 2e92d69f44..432d2440ad 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/App.xaml.cs @@ -5,16 +5,50 @@ namespace Boilerplate.Client.Maui; public partial class App { private readonly Page mainPage; + private readonly IExceptionHandler exceptionHandler; + private readonly IBitDeviceCoordinator deviceCoordinator; + private readonly IStringLocalizer<AppStrings> localizer; - public App(MainPage mainPage) + public App(MainPage mainPage, + IExceptionHandler exceptionHandler, + IBitDeviceCoordinator deviceCoordinator, + IStorageService storageService, + IStringLocalizer<AppStrings> localizer) { - InitializeComponent(); - + this.exceptionHandler = exceptionHandler; + this.deviceCoordinator = deviceCoordinator; this.mainPage = new NavigationPage(mainPage); + this.localizer = localizer; + + InitializeComponent(); } protected override Window CreateWindow(IActivationState? activationState) { return new Window(mainPage) { }; } + + protected async override void OnStart() + { + try + { + base.OnStart(); + + await deviceCoordinator.ApplyTheme(AppInfo.Current.RequestedTheme is AppTheme.Dark); + +//-:cnd:noEmit +#if Android + if (Version.TryParse(Android.Webkit.WebView.CurrentWebViewPackage?.VersionName, out var webViewVersion) && + webViewVersion.Major < 83) + { + await App.Current!.Windows.First().Page!.DisplayAlert("Boilerplate", localizer[nameof(AppStrings.UpdateWebViewThroughGooglePlay)], localizer[nameof(AppStrings.Ok)]); + await Launcher.OpenAsync($"https://play.google.com/store/apps/details?id={Android.Webkit.WebView.CurrentWebViewPackage.PackageName}"); + } +#endif + } + catch (Exception exp) + { + exceptionHandler.Handle(exp); + } + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj index 32d6de8577..4238d940e4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj @@ -16,14 +16,13 @@ <!-- App Identifier --> <ApplicationId>com.bitplatform.template</ApplicationId> - <ApplicationIdGuid>AC87AA5B-4B37-4E52-8468-2D5DF24AF256</ApplicationIdGuid> <!-- Versions --> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> <ApplicationVersion>1</ApplicationVersion> <!-- Required for C# Hot Reload --> - <UseInterpreter Condition="'$(Configuration)' == 'Debug'">True</UseInterpreter> + <UseInterpreter Condition="'$(Environment)' == 'Development'">True</UseInterpreter> <NoWarn>$(NoWarn);ClassWithoutModifierAnalyzer</NoWarn> @@ -33,7 +32,7 @@ </ResolveStaticWebAssetsInputsDependsOn> </PropertyGroup> - <PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Configuration)' == 'Release'"> + <PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Environment)' == 'Production'"> <EnableLLVM>true</EnableLLVM> <RuntimeIdentifiers Condition="'$(AndroidPackageFormat)' == 'apk'">android-arm64</RuntimeIdentifiers> <MauiUseDefaultAotProfile Condition="Exists('custom.aprof')">false</MauiUseDefaultAotProfile> @@ -43,12 +42,16 @@ <!--/-:msbuild-conditional:noEmit --> </PropertyGroup> - <PropertyGroup Condition="$(TargetFramework.Contains('-ios'))"> + <PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Environment)' == 'Development'"> + <CodesignEntitlements>Platforms/iOS/Entitlements.Development.plist</CodesignEntitlements> <EnableSGenConc>True</EnableSGenConc> </PropertyGroup> - <PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'"> + <PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Environment)' == 'Production'"> + <CodesignEntitlements>Platforms/iOS/Entitlements.Production.plist</CodesignEntitlements> + <EnableSGenConc>True</EnableSGenConc> <!--/+:msbuild-conditional:noEmit --> + <!-- https://learn.microsoft.com/en-us/dotnet/maui/macios/interpreter --> <MtouchInterpreter Condition=" '$(offlineDb)' == 'false'">-all</MtouchInterpreter> <UseInterpreter Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''">True</UseInterpreter> <PublishAot Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''">false</PublishAot> @@ -57,12 +60,12 @@ <!-- Build Properties must be defined within these property groups to ensure successful publishing to the Mac App Store. See: https://aka.ms/maui-publish-app-store#define-build-properties-in-your-project-file --> - <PropertyGroup Condition="$(TargetFramework.Contains('-maccatalyst')) and '$(Configuration)' == 'Debug'"> - <CodesignEntitlements>Platforms/MacCatalyst/Entitlements.Debug.plist</CodesignEntitlements> + <PropertyGroup Condition="$(TargetFramework.Contains('-maccatalyst')) and '$(Environment)' == 'Development'"> + <CodesignEntitlements>Platforms/MacCatalyst/Entitlements.Development.plist</CodesignEntitlements> </PropertyGroup> - <PropertyGroup Condition="$(TargetFramework.Contains('-maccatalyst')) and '$(Configuration)' == 'Release'"> - <CodesignEntitlements>Platforms/MacCatalyst/Entitlements.Release.plist</CodesignEntitlements> + <PropertyGroup Condition="$(TargetFramework.Contains('-maccatalyst')) and '$(Environment)' == 'Production'"> + <CodesignEntitlements>Platforms/MacCatalyst/Entitlements.Production.plist</CodesignEntitlements> <UseHardenedRuntime>true</UseHardenedRuntime> <!--/+:msbuild-conditional:noEmit --> <MtouchInterpreter Condition=" '$(offlineDb)' == 'false'">-all</MtouchInterpreter> @@ -71,9 +74,24 @@ <!--/-:msbuild-conditional:noEmit --> </PropertyGroup> - <ItemGroup Condition="$(TargetFramework.Contains('-android'))"> + <AndroidResource Include="Platforms\Android\Resources\**\styles.xml" /> + <GoogleServicesJson Condition="Exists('Platforms\Android\google-services.json')" Include="Platforms\Android\google-services.json" /> <AndroidAotProfile Condition="Exists('custom.aprof')" Include="custom.aprof" /> + <!--/+:msbuild-conditional:noEmit --> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.Firebase.Messaging" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Collection" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Collection.Ktx" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModel" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModel.Ktx" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModelSavedState" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.Runtime" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.Runtime.Ktx" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Activity" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Activity.Ktx" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.LiveData" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.LiveData.Core.Ktx" /> + <!--/-:msbuild-conditional:noEmit --> <!-- Run the following commands to create custom.aprof file which improves the Android app performance --> <!-- dotnet add package Mono.AotProfiler.Android --> <!-- dotnet build -t:BuildAndStartAotProfiling -f net8.0-android -p:UseInterpreter=false --> @@ -89,6 +107,7 @@ <Using Include="Microsoft.AspNetCore.Components.WebView.Maui" /> <Using Include="Boilerplate.Client.Core.Components.Layout" /> + <Using Include="Boilerplate.Client.Core.Components.Pages" /> <Using Include="Boilerplate.Client.Core.Services.Contracts" /> <Using Include="Boilerplate.Client.Core.Services" /> <Using Include="Boilerplate.Shared" /> @@ -117,11 +136,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> <Content Remove="compilerconfig.json" /> <None Include="compilerconfig.json" /> - <Content Remove="Properties\launchSettings.json" /> + <Content Remove="appsettings*.json" /> + <EmbeddedResource Include="appsettings*.json" /> </ItemGroup> <ItemGroup> @@ -129,24 +147,18 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="EmbedIO" Version="3.5.2" /> - <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.82" /> - <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.82" /> - <PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.82" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.82" /> - <PackageReference Include="Oscore.Maui.Android.InAppUpdates" Version="1.1.0" /> - <PackageReference Include="Oscore.Maui.AppStoreInfo" Version="1.0.7" /> - <PackageReference Include="Oscore.Maui.InAppReviews" Version="1.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.EventSource" Version="8.0.0" /> + <PackageReference Include="EmbedIO" /> + <PackageReference Include="Microsoft.Maui.Controls" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" /> + <PackageReference Include="Microsoft.Extensions.Logging.EventLog" /> + <PackageReference Include="Microsoft.Extensions.Logging.EventSource" /> <!--/+:msbuild-conditional:noEmit --> - <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Analytics" Version="5.0.5" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Crashes" Version="5.0.5" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="West.Extensions.Logging.AppCenter" Version="2.2.2" /> - <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.22.0" /> + <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Analytics" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Crashes" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="West.Extensions.Logging.AppCenter" /> + <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.Extensions.Logging.ApplicationInsights" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Plugin.LocalNotification" /> <!--/-:msbuild-conditional:noEmit --> </ItemGroup> @@ -156,7 +168,14 @@ </Target> <Target Name="BuildCssFiles"> - <Exec Command="../Boilerplate.Client.Core/node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="../Boilerplate.Client.Core/node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + </Target> + + <!-- https://github.com/dotnet/runtime/issues/104599 --> + <Target Name="_FixAndroidAotInputs" DependsOnTargets="_AndroidAotInputs" BeforeTargets="_AndroidAotCompilation"> + <ItemGroup Condition="$(EnableLLVM)"> + <_AndroidAotInputs Remove="$(IntermediateLinkDir)**\System.Net.Sockets.dll" /> + </ItemGroup> </Target> </Project> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/ClientMauiSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/ClientMauiSettings.cs new file mode 100644 index 0000000000..2baad82fd2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/ClientMauiSettings.cs @@ -0,0 +1,9 @@ +//+:cnd:noEmit +using Boilerplate.Client.Core; + +namespace Boilerplate.Client.Maui; + +public class ClientMauiSettings : ClientCoreSettings +{ + +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor index d0bfb60a17..3035e0431e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor @@ -1,44 +1,24 @@ @attribute [Route(Urls.AboutPage)] @attribute [Route("{culture?}" + Urls.AboutPage)] -@inherits AppComponentBase +@inherits AppPageBase -<div class="page-container"> - <section class="page-section about-section"> - <h1 class="about-section-title">About</h1> - - <div class="about-section-desc"> +<section> + <BitStack Gap="3rem"> + <BitText Typography="BitTypography.Body1"> For Razor pages that are exclusively dependent on native Maui features for Android, iOS, Windows, and macOS functionality, - <br /> consider using Client/Maui project instead of placing them in Client/Core project. <br /> This approach allows direct access to native features without the need for dependency injection (DI) or publish-subscribe messaging patterns. - </div> + </BitText> - <BitStack VerticalAlign="BitAlignment.Center" HorizontalAlign="BitAlignment.SpaceAround"> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>App Name:</BitLabel> - <BitLabel>@appName</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>App Version:</BitLabel> - <BitLabel>@appVersion</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>OS:</BitLabel> - <BitLabel>@os</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>OEM:</BitLabel> - <BitLabel>@oem</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>Environment:</BitLabel> - <BitLabel>@AppEnvironment.Current</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>Process Id:</BitLabel> - <BitLabel>@processId</BitLabel> - </BitStack> + <BitStack AutoWidth> + <BitText>App Name: <b>@appName</b></BitText> + <BitText>App Version: <b>@appVersion</b></BitText> + <BitText>OS: <b>@os</b></BitText> + <BitText>Web View: <b>@webView</b></BitText> + <BitText>OEM: <b>@oem</b></BitText> + <BitText>Environment: <b>@AppEnvironment.Current</b></BitText> + <BitText>Process Id: <b>@processId</b></BitText> </BitStack> - </section> -</div> \ No newline at end of file + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.cs index 1587f509a9..0346bc950c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.cs @@ -1,28 +1,32 @@ -using System.Diagnostics; +//-:cnd:noEmit namespace Boilerplate.Client.Maui.Components.Pages; public partial class AboutPage { + [AutoInject] private ITelemetryContext telemetryContext = default!; + + protected override string? Title => Localizer[nameof(AppStrings.AboutTitle)]; + protected override string? Subtitle => string.Empty; + + private string appName = default!; private string appVersion = default!; private string processId = default!; private string os = default!; + private string webView = default!; private string oem = default!; protected async override Task OnInitAsync() { - appName = AppInfo.Name; -#if Android // You have direct access to the Android, iOS, macOS, and Windows SDK features along with the ability to // call third-party Java, Kotlin, Swift, and Objective-C libraries. // https://stackoverflow.com/a/2941199/2720104 - appVersion = MauiApplication.Current.PackageManager!.GetPackageInfo(MauiApplication.Current.PackageName!, 0)!.VersionName!; -#else - appVersion = AppInfo.Version.ToString(); -#endif + appName = AppInfo.Name; + appVersion = telemetryContext.AppVersion!; + os = telemetryContext.OS!; + webView = telemetryContext.WebView!; processId = Environment.ProcessId.ToString(); - os = $"{DeviceInfo.Current.Platform} {DeviceInfo.Current.VersionString}"; oem = DeviceInfo.Current.Manufacturer; await base.OnInitAsync(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.scss index fa443148f8..07c3d06a8b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Components/Pages/AboutPage.razor.scss @@ -1,86 +1,5 @@ -@import "../../../Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss"; +//@import "../../../Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss"; -.page-container { - width: 100%; - display: flex; - align-items: center; - padding: 0 rem2(16px); - flex-flow: column nowrap; - justify-content: flex-start; -} - -::deep * { - direction: ltr; -} - -.page-section { - width: 100%; -} - -.about-section { - width: 100%; - display: flex; - align-items: center; - padding: rem2(144px) 0; - flex-flow: column nowrap; - justify-content: flex-start; - - @include lt-xl { - padding: rem2(130px) 0; - } - - @include sm { - padding: rem2(120px) 0; - } -} - -.about-section-title { - font-weight: bold; - text-align: center; - font-size: rem2(42px); - line-height: rem2(62px); - margin-bottom: rem2(32px); - - @include lg { - font-size: rem2(34px); - line-height: rem2(56px); - margin-bottom: rem2(24px); - } - - @include md { - font-size: rem2(26px); - line-height: rem2(44px); - margin-bottom: rem2(24px); - } - - @include sm { - font-size: rem2(24px); - line-height: rem2(40px); - margin-bottom: rem2(16px); - } -} - -.about-section-desc { - font-size: rem2(16px); - max-width: rem2(1232px); - line-height: rem2(40px); - margin-bottom: rem2(40px); - - @include lg { - max-width: rem2(821px); - line-height: rem2(32px); - margin-bottom: rem2(38px); - } - - @include md { - max-width: rem2(572px); - line-height: rem2(28px); - margin-bottom: rem2(32px); - } - - @include sm { - max-width: 100%; - line-height: rem2(24px); - margin-bottom: rem2(32px); - } +section { + padding: 1rem; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml index 6947f39d3a..c7cf69de04 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui" - xmlns:core="clr-namespace:Boilerplate.Client.Core;assembly=Boilerplate.Client.Core" + xmlns:core="clr-namespace:Boilerplate.Client.Core.Components;assembly=Boilerplate.Client.Core" xmlns:themeColors="clr-namespace:Boilerplate.Client.Core.Styles;assembly=Boilerplate.Client.Core" BackgroundColor="{AppThemeBinding Light={x:Static themeColors:ThemeColors.PrimaryLightBgColor}, Dark={x:Static themeColors:ThemeColors.PrimaryDarkBgColor}}"> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml.cs index 5c1891212f..0d4b8801b4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MainPage.xaml.cs @@ -1,82 +1,18 @@ -//-:cnd:noEmit -using Maui.AppStores; -using Maui.InAppReviews; +//+:cnd:noEmit namespace Boilerplate.Client.Maui; public partial class MainPage { - private readonly IExceptionHandler exceptionHandler; - private readonly IBitDeviceCoordinator deviceCoordinator; - private readonly IStorageService storageService; - - public MainPage(IExceptionHandler exceptionHandler, - IBitDeviceCoordinator deviceCoordinator, - IPubSubService pubSubService, - IStorageService storageService) + public MainPage(ClientMauiSettings clientMauiSettings) { - this.exceptionHandler = exceptionHandler; - this.deviceCoordinator = deviceCoordinator; - this.storageService = storageService; - InitializeComponent(); - - pubSubService.Subscribe(PubSubMessages.USER_DATA_UPDATED, async _ => + //#if (appInsights == true) + AppWebView.RootComponents.Insert(0, new() { - // It's an opportune moment to request a store review. (: - - await Dispatcher.DispatchAsync(async () => - { - if ((await storageService.GetItem("StoreReviewRequested")) is not "true") - { - await storageService.SetItem("StoreReviewRequested", "true"); - ReviewStatus status = await InAppReview.Current.RequestAsync(); - } - }); + ComponentType = typeof(BlazorApplicationInsights.ApplicationInsightsInit), + Selector = "head::after" }); - } - - protected override async void OnAppearing() - { - try - { - base.OnAppearing(); - - await deviceCoordinator.ApplyTheme(AppInfo.Current.RequestedTheme is AppTheme.Dark); - - await CheckForUpdates(); - } - catch (Exception exp) - { - exceptionHandler.Handle(exp); - } - } - - private async Task CheckForUpdates() - { - if (AppPlatform.IsAndroid) // We're using in app updates for android thanks to Oscore.Maui.Android.InAppUpdates - return; - - await Task.Delay(TimeSpan.FromSeconds(3)); // No rush to check for updates. - - try - { - if (await AppStoreInfo.Current.IsUsingLatestVersionAsync() is false) - { - if (await storageService.GetItem($"{AppInfo.Version}_UpdateFromVersionIsRequested") is not "true") - { - await storageService.SetItem($"{AppInfo.Version}_UpdateFromVersionIsRequested", "true"); - - // It's an opportune moment to request an update. (: - // https://github.com/oscoreio/Maui.AppStoreInfo - if (await DisplayAlert(AppStrings.NewVersionIsAvailable, AppStrings.UpdateToNewVersion, AppStrings.Yes, AppStrings.No) is true) - { - await AppStoreInfo.Current.OpenApplicationInStoreAsync(); - } - } - } - } - catch (InvalidOperationException) when ((AppPlatform.IsIOS || AppPlatform.IsMacOS) && AppEnvironment.IsDev()) { } - catch (FileNotFoundException) { } + //#endif } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs index 7c893a1c72..7d247368e3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.Services.cs @@ -1,5 +1,5 @@ -using Boilerplate.Client.Maui.Services; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; +using Boilerplate.Client.Maui.Services; namespace Boilerplate.Client.Maui; @@ -8,30 +8,18 @@ public static partial class MauiProgram public static void ConfigureServices(this MauiAppBuilder builder) { // Services being registered here can get injected in Maui (Android, iOS, macOS, Windows) - var services = builder.Services; var configuration = builder.Configuration; + services.AddClientCoreProjectServices(builder.Configuration); -#if Android - services.AddClientMauiProjectAndroidServices(); -#elif iOS - services.AddClientMauiProjectIosServices(); -#elif Mac - services.AddClientMauiProjectMacCatalystServices(); -#elif Windows - services.AddClientMauiProjectWindowsServices(); -#endif - - services.AddMauiBlazorWebView(); - - if (AppEnvironment.IsDev()) - { - services.AddBlazorWebViewDeveloperTools(); - } + services.AddTransient<MainPage>(); - services.TryAddSingleton(sp => + services.AddScoped<IExceptionHandler, MauiExceptionHandler>(); + services.AddScoped<IBitDeviceCoordinator, MauiDeviceCoordinator>(); + services.AddScoped<IExternalNavigationService, MauiExternalNavigationService>(); + services.AddScoped(sp => { - var handler = sp.GetRequiredKeyedService<DelegatingHandler>("DefaultMessageHandler"); + var handler = sp.GetRequiredService<HttpMessageHandler>(); HttpClient httpClient = new(handler) { BaseAddress = new Uri(configuration.GetServerAddress(), UriKind.Absolute) @@ -39,58 +27,65 @@ public static void ConfigureServices(this MauiAppBuilder builder) return httpClient; }); - builder.Logging.AddConfiguration(configuration.GetSection("Logging")); - - if (AppEnvironment.IsDev()) + services.AddSingleton<IStorageService, MauiStorageService>(); + services.AddSingleton(sp => configuration.Get<ClientMauiSettings>()!); + services.AddSingleton(ITelemetryContext.Current!); + if (AppPlatform.IsWindows || AppPlatform.IsMacOS) { - builder.Logging.AddDebug(); + services.AddSingleton<ILocalHttpServer, MauiLocalHttpServer>(); } - builder.Logging.AddConsole(); + services.AddMauiBlazorWebView(); + services.AddBlazorWebViewDeveloperTools(); + + builder.Logging.ConfigureLoggers(); + builder.Logging.AddConfiguration(configuration.GetSection("Logging")); + + builder.Logging.AddEventSourceLogger(); if (AppPlatform.IsWindows) { builder.Logging.AddEventLog(); } - builder.Logging.AddEventSourceLogger(); - //+:cnd:noEmit //#if (appCenter == true) if (Microsoft.AppCenter.AppCenter.Configured) { - builder.Logging.AddAppCenter(options => { }); + builder.Logging.AddAppCenter(options => options.IncludeScopes = true); } //#endif //#if (appInsights == true) - builder.Logging.AddApplicationInsights(config => + var connectionString = configuration.Get<ClientMauiSettings>()!.ApplicationInsights?.ConnectionString; + if (string.IsNullOrEmpty(connectionString) is false) { - config.TelemetryInitializers.Add(new MauiTelemetryInitializer()); - var connectionString = configuration["ApplicationInsights:ConnectionString"]; - if (string.IsNullOrEmpty(connectionString) is false) + builder.Logging.AddApplicationInsights(config => { + config.TelemetryInitializers.Add(new MauiAppInsightsTelemetryInitializer()); config.ConnectionString = connectionString; - } - }, options => - { - options.IncludeScopes = true; - }); + }, options => + { + options.IncludeScopes = true; + }); + } //#endif //-:cnd:noEmit - services.TryAddTransient<MainPage>(); - services.TryAddTransient<IStorageService, MauiStorageService>(); - services.TryAddSingleton<IBitDeviceCoordinator, MauiDeviceCoordinator>(); - services.TryAddTransient<IExceptionHandler, MauiExceptionHandler>(); - services.TryAddTransient<IExternalNavigationService, MauiExternalNavigationService>(); - - if (AppPlatform.IsWindows || AppPlatform.IsMacOS) - { - services.AddSingleton<ILocalHttpServer, MauiLocalHttpServer>(); - } + services.AddOptions<ClientMauiSettings>() + .Bind(configuration) + .ValidateDataAnnotations() + .ValidateOnStart(); - services.AddClientCoreProjectServices(); +#if Android + services.AddClientMauiProjectAndroidServices(builder.Configuration); +#elif iOS + services.AddClientMauiProjectIosServices(builder.Configuration); +#elif Mac + services.AddClientMauiProjectMacCatalystServices(builder.Configuration); +#elif Windows + services.AddClientMauiProjectWindowsServices(builder.Configuration); +#endif } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.cs index 8d46e8600d..eaf601ee64 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/MauiProgram.cs @@ -1,11 +1,13 @@ //-:cnd:noEmit -using Maui.AppStores; -using Maui.InAppReviews; -using Maui.Android.InAppUpdates; +using Microsoft.Maui.Platform; using Microsoft.Maui.LifecycleEvents; +//+:cnd:noEmit +//#if (notification == true) +using Plugin.LocalNotification; +//#endif +//-:cnd:noEmit using Boilerplate.Client.Core.Styles; using Boilerplate.Client.Maui.Services; -using Microsoft.Maui.Platform; #if iOS || Mac using UIKit; using WebKit; @@ -32,17 +34,22 @@ public static MauiApp CreateMauiApp() #if iOS AppPlatform.IsIosOnMacOS = NSProcessInfo.ProcessInfo.IsiOSApplicationOnMac; #endif - - AppPlatform.OSDescription = $"{DeviceInfo.Current.Manufacturer} {(AppPlatform.IsIosOnMacOS ? DevicePlatform.macOS : DeviceInfo.Current.Platform)} {DeviceInfo.Current.Version}"; + ITelemetryContext.Current = new MauiTelemetryContext(); var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() - .UseAndroidInAppUpdates() - .UseInAppReviews() - .UseAppStoreInfo() - .Configuration.AddClientConfigurations(); + .Configuration.AddClientConfigurations(clientEntryAssemblyName: "Boilerplate.Client.Maui"); + + //+:cnd:noEmit + //#if (notification == true) + if (AppPlatform.IsWindows is false) + { + builder.UseLocalNotification(); + } + //#endif + //-:cnd:noEmit builder.ConfigureServices(); @@ -57,7 +64,7 @@ bool HandleAppLink(NSUserActivity? userActivity) { var url = $"{userActivity.WebPageUrl.Path}?{userActivity.WebPageUrl.Query}"; - _ = Core.Routes.OpenUniversalLink(url); + _ = Core.Components.Routes.OpenUniversalLink(url); return true; } @@ -101,20 +108,25 @@ private static void SetupBlazorWebView() #if Windows webView.DefaultBackgroundColor = Color.FromArgb(webViewBackgroundColor).ToWindowsColor(); - if (AppEnvironment.IsDev() is false) - { - webView.EnsureCoreWebView2Async() - .AsTask() - .ContinueWith(async _ => + webView.EnsureCoreWebView2Async() + .AsTask() + .ContinueWith(async _ => + { + await Application.Current!.Dispatcher.DispatchAsync(() => { - await Application.Current!.Dispatcher.DispatchAsync(() => + webView.CoreWebView2.PermissionRequested += async (sender, args) => + { + args.Handled = true; + args.State = Microsoft.Web.WebView2.Core.CoreWebView2PermissionState.Allow; + }; + if (AppEnvironment.IsDev() is false) { var settings = webView.CoreWebView2.Settings; settings.IsZoomControlEnabled = false; settings.AreBrowserAcceleratorKeysEnabled = false; - }); + } }); - } + }); #elif iOS || Mac webView.NavigationDelegate = new CustomWKNavigationDelegate(); @@ -124,13 +136,10 @@ private static void SetupBlazorWebView() webView.ScrollView.Bounces = false; webView.Opaque = false; - if (AppEnvironment.IsDev()) + if ((DeviceInfo.Current.Platform == DevicePlatform.MacCatalyst && DeviceInfo.Current.Version >= new Version(13, 3)) + || (DeviceInfo.Current.Platform == DevicePlatform.iOS && DeviceInfo.Current.Version >= new Version(16, 4))) { - if ((DeviceInfo.Current.Platform == DevicePlatform.MacCatalyst && DeviceInfo.Current.Version >= new Version(13, 3)) - || (DeviceInfo.Current.Platform == DevicePlatform.iOS && DeviceInfo.Current.Version >= new Version(16, 4))) - { - webView.SetValueForKey(NSObject.FromObject(true), new NSString("inspectable")); - } + webView.SetValueForKey(NSObject.FromObject(true), new NSString("inspectable")); } #elif Android webView.SetBackgroundColor(Android.Graphics.Color.ParseColor(webViewBackgroundColor)); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml index 458e80e9f0..160ca5411f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-sdk android:targetSdkVersion="35" /> <application android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round"></application> </manifest> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IAndroidServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IAndroidServiceCollectionExtensions.cs new file mode 100644 index 0000000000..6551f3c39c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IAndroidServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +//+:cnd:noEmit +//#if (notification == true) +using Boilerplate.Client.Maui.Platforms.Android.Services; +//#endif + +namespace Microsoft.Extensions.DependencyInjection; + +public static partial class IAndroidServiceCollectionExtensions +{ + public static IServiceCollection AddClientMauiProjectAndroidServices(this IServiceCollection services, IConfiguration configuration) + { + // Services being registered here can get injected in Maui/Android. + + //#if (notification == true) + services.AddSingleton<IPushNotificationService, AndroidPushNotificationService>(); + //#endif + + return services; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 1a0624651d..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -public static partial class IServiceCollectionExtensions -{ - public static IServiceCollection AddClientMauiProjectAndroidServices(this IServiceCollection services) - { - // Services being registered here can get injected in Maui/Android. - - return services; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs index 84f16a20fe..7a4ec535d7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs @@ -3,8 +3,12 @@ using Android.App; using Android.Content; using Android.Content.PM; -using Boilerplate.Client.Core; using Java.Net; +//#if (notification == true) +using Android.Gms.Tasks; +using Firebase.Messaging; +//#endif +using Boilerplate.Client.Core.Components; namespace Boilerplate.Client.Maui.Platforms.Android; @@ -12,9 +16,10 @@ namespace Boilerplate.Client.Maui.Platforms.Android; DataSchemes = ["https", "http"], DataHosts = ["use-your-server-url-here.com"], // the following app links will be opened in app instead of browser if the app is installed on Android device. - DataPaths = ["/"], + DataPaths = [Urls.HomePage], DataPathPrefixes = [ - Urls.ConfirmPage, Urls.ForgotPasswordPage, Urls.ProfilePage, Urls.ResetPasswordPage, Urls.SignInPage, Urls.SignUpPage, Urls.NotAuthorizedPage, Urls.NotFoundPage, Urls.TermsPage, Urls.AboutPage, + "/en-US", "/en-GB", "/fa-IR", "/nl-NL", + Urls.ConfirmPage, Urls.ForgotPasswordPage, Urls.SettingsPage, Urls.ResetPasswordPage, Urls.SignInPage, Urls.SignUpPage, Urls.NotAuthorizedPage, Urls.NotFoundPage, Urls.TermsPage, Urls.AboutPage, //#if (sample == "Admin") Urls.AddOrEditCategoryPage, Urls.CategoriesPage, Urls.DashboardPage, Urls.ProductsPage, //#elif (sample == "Todo") @@ -30,9 +35,19 @@ namespace Boilerplate.Client.Maui.Platforms.Android; [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public partial class MainActivity : MauiAppCompatActivity + //#if (notification == true) + , IOnSuccessListener + //#endif { + //#if (notification == true) + private IPushNotificationService PushNotificationService => IPlatformApplication.Current!.Services.GetRequiredService<IPushNotificationService>(); + //#endif + protected override void OnCreate(Bundle? savedInstanceState) { + // https://github.com/dotnet/maui/issues/24742 + Theme?.ApplyStyle(Resource.Style.OptOutEdgeToEdgeEnforcement, force: false); + base.OnCreate(savedInstanceState); var url = Intent?.DataString; @@ -40,6 +55,15 @@ protected override void OnCreate(Bundle? savedInstanceState) { _ = Routes.OpenUniversalLink(new URL(url).File ?? Urls.HomePage); } + //#if (notification == true) + PushNotificationService.IsPushNotificationSupported(default).ContinueWith(task => + { + if (task.Result) + { + FirebaseMessaging.Instance.GetToken().AddOnSuccessListener(this); + } + }); + //#endif } protected override void OnNewIntent(Intent? intent) @@ -53,4 +77,11 @@ protected override void OnNewIntent(Intent? intent) _ = Routes.OpenUniversalLink(new URL(url).File ?? Urls.HomePage); } } + + //#if (notification == true) + public void OnSuccess(Java.Lang.Object? result) + { + PushNotificationService.Token = result!.ToString(); + } + //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainApplication.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainApplication.cs index 1692a9dc7f..77c9433661 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainApplication.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainApplication.cs @@ -5,10 +5,20 @@ [assembly: UsesPermission(Android.Manifest.Permission.Internet)] [assembly: UsesPermission(Android.Manifest.Permission.AccessNetworkState)] +//+:cnd:noEmit +//#if (notification == true) +// https://github.com/thudugala/Plugin.LocalNotification/wiki/1.-Usage-10.0.0--.Net-MAUI#android-specific-setup +[assembly: UsesPermission(Android.Manifest.Permission.PostNotifications)] +[assembly: UsesPermission(Android.Manifest.Permission.Vibrate)] +[assembly: UsesPermission(Android.Manifest.Permission.WakeLock)] +[assembly: UsesPermission(Android.Manifest.Permission.ReceiveBootCompleted)] +//#endif +//-:cnd:noEmit + namespace Boilerplate.Client.Maui.Platforms.Android; [Application( -#if DEBUG +#if Development UsesCleartextTraffic = true, #endif AllowBackup = true, diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml new file mode 100644 index 0000000000..f7567fa256 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<resources> + <style name="OptOutEdgeToEdgeEnforcement"> + <!-- https://github.com/dotnet/maui/issues/24742 --> + </style> +</resources> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml new file mode 100644 index 0000000000..955b6c267c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" ?> +<resources> + <style name="OptOutEdgeToEdgeEnforcement"> + <!-- https://github.com/dotnet/maui/issues/24742 --> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> +</resources> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs new file mode 100644 index 0000000000..260a3f13a9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs @@ -0,0 +1,55 @@ +using Android.Gms.Common; +using Plugin.LocalNotification; +using static Android.Provider.Settings; +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Maui.Platforms.Android.Services; + +public partial class AndroidPushNotificationService : PushNotificationServiceBase +{ + public async override Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken) + { + return await MainThread.InvokeOnMainThreadAsync(async () => + { + if (await LocalNotificationCenter.Current.AreNotificationsEnabled() is false) + { + await LocalNotificationCenter.Current.RequestNotificationPermission(); + } + + return await LocalNotificationCenter.Current.AreNotificationsEnabled() && + GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Platform.AppContext) == ConnectionResult.Success; + }); + } + + public string GetDeviceId() => Secure.GetString(Platform.AppContext.ContentResolver, Secure.AndroidId)!; + + public override async Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) + { + try + { + using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15)); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token); + + while (string.IsNullOrEmpty(Token)) + { + // After the NotificationsSupported Task completes with a result of true, + // we use FirebaseMessaging.Instance.GetToken. + // This method is asynchronous and we need to wait for it to complete. + await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token); + } + } + catch (Exception exp) + { + throw new InvalidOperationException("Unable to resolve token for FCMv1.", exp); + } + + var installation = new DeviceInstallationDto + { + InstallationId = GetDeviceId(), + Platform = "fcmV1", + PushChannel = Token + }; + + return installation; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/PushNotificationFirebaseMessagingService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/PushNotificationFirebaseMessagingService.cs new file mode 100644 index 0000000000..02840ebf82 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/PushNotificationFirebaseMessagingService.cs @@ -0,0 +1,58 @@ +using Android.App; +using Android.Content; +using Firebase.Messaging; +using Plugin.LocalNotification; + +namespace Boilerplate.Client.Maui.Platforms.Android.Services; + +[Service(Exported = false)] +[IntentFilter(["com.google.firebase.MESSAGING_EVENT"])] +public partial class PushNotificationFirebaseMessagingService : FirebaseMessagingService +{ + private IPushNotificationService PushNotificationService => IPlatformApplication.Current!.Services.GetRequiredService<IPushNotificationService>(); + + public override async void OnNewToken(string token) + { + try + { + PushNotificationService.Token = token; + + await PushNotificationService.RegisterDevice(default); + } + catch (Exception exp) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(exp); + } + } + + public override async void OnMessageReceived(RemoteMessage message) + { + try + { + base.OnMessageReceived(message); + + var services = IPlatformApplication.Current!.Services; + var localizer = services.GetRequiredService<IStringLocalizer<AppStrings>>(); + + // Use the following code to get the action value from the push notification. + // message.Data.TryGetValue("action", out var messageAction); + + var notification = message.GetNotification(); + var title = notification!.Title; + var body = notification.Body; + + if (string.IsNullOrEmpty(title) is false) + { + await LocalNotificationCenter.Current.Show(new() + { + Title = title!, + Description = body! + }); + } + } + catch (Exception exp) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(exp); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/AppDelegate.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/AppDelegate.cs index f2070bdcf2..78234c739f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/AppDelegate.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/AppDelegate.cs @@ -1,4 +1,10 @@ -using Foundation; +//+:cnd:noEmit +using Foundation; +using UIKit; +//#if (notification == true) +using UserNotifications; +using Boilerplate.Client.Maui.Platforms.MacCatalyst.Services; +//#endif namespace Boilerplate.Client.Maui.Platforms.MacCatalyst; @@ -6,4 +12,54 @@ namespace Boilerplate.Client.Maui.Platforms.MacCatalyst; public partial class AppDelegate : MauiUIApplicationDelegate { protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + //#if (notification == true) + private IPushNotificationService NotificationService => IPlatformApplication.Current!.Services.GetRequiredService<IPushNotificationService>(); + + [Export("application:didFinishLaunchingWithOptions:")] + //#endif + public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + { + //#if (notification == true) + NotificationService.IsPushNotificationSupported(default).ContinueWith(task => + { + if (task.Result) + { + MainThread.BeginInvokeOnMainThread(() => + { + UIApplication.SharedApplication.RegisterForRemoteNotifications(); + UNUserNotificationCenter.Current.Delegate = new AppUNUserNotificationCenterDelegate(); + }); + } + }); + + // Use the following code the get the action value from the push notification when the app is launched by tapping on the push notification. + // using var userInfo = launchOptions?.ObjectForKey(UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary; + // var actionValue = userInfo?.ObjectForKey(new NSString("action")) as NSString; + //#endif + + return base.FinishedLaunching(application, launchOptions!); + } + + //#if (notification == true) + [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")] + public async void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) + { + try + { + NotificationService.Token = deviceToken.ToHexString()!; + await NotificationService.RegisterDevice(default); + } + catch (Exception exp) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(exp); + } + } + + [Export("application:didFailToRegisterForRemoteNotificationsWithError:")] + public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(new InvalidOperationException(error.Description.ToString())); + } + //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Debug.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Development.plist similarity index 75% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Debug.plist rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Development.plist index 674d08a59a..ec087ad6d2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Debug.plist +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Development.plist @@ -6,5 +6,9 @@ <!-- See https://aka.ms/blazor-hybrid-developer-tools --> <key>com.apple.security.get-task-allow</key> <true /> + <!--#if (notification == true)--> + <key>aps-environment</key> + <string>development</string> + <!--#endif--> </dict> </plist> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Release.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Production.plist similarity index 79% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Release.plist rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Production.plist index d846bd476c..e7daecb1a1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Release.plist +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Entitlements.Production.plist @@ -10,5 +10,9 @@ <true /> <key>com.apple.security.cs.allow-jit</key> <true/> + <!--#if (notification == true)--> + <key>aps-environment</key> + <string>production</string> + <!--#endif--> </dict> </plist> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IMacServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IMacServiceCollectionExtensions.cs new file mode 100644 index 0000000000..b826496109 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IMacServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +//+:cnd:noEmit +//#if (notification == true) +using Boilerplate.Client.Maui.Platforms.MacCatalyst.Services; +//#endif + +namespace Microsoft.Extensions.DependencyInjection; + +public static partial class IMacServiceCollectionExtensions +{ + public static IServiceCollection AddClientMauiProjectMacCatalystServices(this IServiceCollection services, IConfiguration configuration) + { + // Services being registered here can get injected in Maui/macOS. + + //#if (notification == true) + services.AddSingleton<IPushNotificationService, MacCatalystPushNotificationService>(); + //#endif + + return services; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index fc1bd2f842..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -public static partial class IServiceCollectionExtensions -{ - public static IServiceCollection AddClientMauiProjectMacCatalystServices(this IServiceCollection services) - { - // Services being registered here can get injected in Maui/macOS. - - return services; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/NSDataExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/NSDataExtensions.cs new file mode 100644 index 0000000000..53b6e96b7c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Extensions/NSDataExtensions.cs @@ -0,0 +1,21 @@ +using System.Text; + +namespace Foundation; + +public static partial class NSDataExtensions +{ + public static string? ToHexString(this NSData data) + { + var bytes = data.ToArray(); + + if (bytes == null) + return null; + + StringBuilder sb = new StringBuilder(bytes.Length * 2); + + foreach (byte b in bytes) + sb.AppendFormat("{0:x2}", b); + + return sb.ToString().ToUpperInvariant(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Info.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Info.plist index 57bad79b0d..39322aa3c4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Info.plist +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Info.plist @@ -43,5 +43,12 @@ </array> <key>XSAppIconAssets</key> <string>Assets.xcassets/appicon.appiconset</string> + <!--#if (notification == true)--> + <key>UIBackgroundModes</key> + <array> + <string>fetch</string> + <string>remote-notification</string> + </array> + <!--#endif--> </dict> </plist> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/AppUNUserNotificationCenterDelegate.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/AppUNUserNotificationCenterDelegate.cs new file mode 100644 index 0000000000..2d30389c1f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/AppUNUserNotificationCenterDelegate.cs @@ -0,0 +1,23 @@ +using UserNotifications; + +namespace Boilerplate.Client.Maui.Platforms.MacCatalyst.Services; +public partial class AppUNUserNotificationCenterDelegate : UNUserNotificationCenterDelegate +{ + public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) + { + // Runs when user taps on push notification. + // Use the following code to get the action value from the tapped push notification. + // var actionValue = response.Notification.Request.Content.UserInfo.ObjectForKey(new NSString("action")) as NSString; + } + + public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) + { + // Displays the notification when the app is in the foreground. + completionHandler(UNNotificationPresentationOptions.Alert | + UNNotificationPresentationOptions.Badge | + UNNotificationPresentationOptions.Sound); + + // Use the following code to get the action value from the push notification. + // var actionValue = notification.Request.Content.UserInfo.ObjectForKey(new NSString("action")) as NSString; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs new file mode 100644 index 0000000000..c286c0771a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs @@ -0,0 +1,53 @@ +using UIKit; +using Plugin.LocalNotification; +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Maui.Platforms.MacCatalyst.Services; + +public partial class MacCatalystPushNotificationService : PushNotificationServiceBase +{ + public async override Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken) + { + return await MainThread.InvokeOnMainThreadAsync(async () => + { + if (await LocalNotificationCenter.Current.AreNotificationsEnabled() is false) + { + await LocalNotificationCenter.Current.RequestNotificationPermission(); + } + + return await LocalNotificationCenter.Current.AreNotificationsEnabled(); + }); + } + + public string GetDeviceId() => UIDevice.CurrentDevice.IdentifierForVendor.ToString(); + + public override async Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) + { + using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15)); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token); + + try + { + while (string.IsNullOrEmpty(Token)) + { + // After the NotificationsSupported Task completes with a result of true, + // we use UNUserNotificationCenter.Current.Delegate. + // This method is asynchronous and we need to wait for it to complete. + await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token); + } + } + catch (Exception exp) + { + throw new InvalidOperationException("Unable to resolve token for APNS.", exp); + } + + var installation = new DeviceInstallationDto + { + InstallationId = GetDeviceId(), + Platform = "apns", + PushChannel = Token + }; + + return installation; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 6c9c355038..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -public static partial class IServiceCollectionExtensions -{ - public static IServiceCollection AddClientMauiProjectWindowsServices(this IServiceCollection services) - { - // Services being registered here can get injected in Maui/windows. - - return services; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IWindowsServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IWindowsServiceCollectionExtensions.cs new file mode 100644 index 0000000000..5ac8d4d057 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Extensions/IWindowsServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +//+:cnd:noEmit +//#if (notification == true) +using Boilerplate.Client.Maui.Platforms.Windows.Services; +//#endif + +namespace Microsoft.Extensions.DependencyInjection; + +public static partial class IWindowsServiceCollectionExtensions +{ + public static IServiceCollection AddClientMauiProjectWindowsServices(this IServiceCollection services, IConfiguration configuration) + { + // Services being registered here can get injected in Maui/windows. + + //#if (notification == true) + services.AddSingleton<IPushNotificationService, WindowsPushNotificationService>(); + //#endif + + return services; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Services/WindowsPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Services/WindowsPushNotificationService.cs new file mode 100644 index 0000000000..3c58938e54 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Windows/Services/WindowsPushNotificationService.cs @@ -0,0 +1,9 @@ +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Maui.Platforms.Windows.Services; + +public partial class WindowsPushNotificationService : PushNotificationServiceBase +{ + public override Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/AppDelegate.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/AppDelegate.cs index 111106e3c6..63b8d75edb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/AppDelegate.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/AppDelegate.cs @@ -1,4 +1,10 @@ -using Foundation; +//+:cnd:noEmit +using Foundation; +using UIKit; +//#if (notification == true) +using UserNotifications; +using Boilerplate.Client.Maui.Platforms.iOS.Services; +//#endif namespace Boilerplate.Client.Maui.Platforms.iOS; @@ -6,4 +12,54 @@ namespace Boilerplate.Client.Maui.Platforms.iOS; public partial class AppDelegate : MauiUIApplicationDelegate { protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + //#if (notification == true) + private IPushNotificationService NotificationService => IPlatformApplication.Current!.Services.GetRequiredService<IPushNotificationService>(); + + [Export("application:didFinishLaunchingWithOptions:")] + //#endif + public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + { + //#if (notification == true) + NotificationService.IsPushNotificationSupported(default).ContinueWith(task => + { + if (task.Result) + { + MainThread.BeginInvokeOnMainThread(() => + { + UIApplication.SharedApplication.RegisterForRemoteNotifications(); + UNUserNotificationCenter.Current.Delegate = new AppUNUserNotificationCenterDelegate(); + }); + } + }); + + // Use the following code the get the action value from the push notification when the app is launched by tapping on the push notification. + // using var userInfo = launchOptions?.ObjectForKey(UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary; + // var actionValue = userInfo?.ObjectForKey(new NSString("action")) as NSString; + //#endif + + return base.FinishedLaunching(application, launchOptions!); + } + + //#if (notification == true) + [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")] + public async void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) + { + try + { + NotificationService.Token = deviceToken.ToHexString()!; + await NotificationService.RegisterDevice(default); + } + catch (Exception exp) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(exp); + } + } + + [Export("application:didFailToRegisterForRemoteNotificationsWithError:")] + public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error) + { + IPlatformApplication.Current!.Services.GetRequiredService<IExceptionHandler>().Handle(new InvalidOperationException(error.Description.ToString())); + } + //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Development.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Development.plist new file mode 100644 index 0000000000..ee53d1ab52 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Development.plist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.associated-domains</key> + <array> + <string>applinks:use-your-server-url-here.com</string> + </array> + <!--#if (notification == true)--> + <key>aps-environment</key> + <string>development</string> + <!--#endif--> +</dict> +</plist> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Production.plist similarity index 72% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.plist rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Production.plist index 342fd30b1f..e6eda9d2f3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.plist +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Production.plist @@ -6,5 +6,9 @@ <array> <string>applinks:use-your-server-url-here.com</string> </array> + <!--#if (notification == true)--> + <key>aps-environment</key> + <string>production</string> + <!--#endif--> </dict> </plist> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IIosServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IIosServiceCollectionExtensions.cs new file mode 100644 index 0000000000..ab7b177b3c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IIosServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +//+:cnd:noEmit +//#if (notification == true) +using Boilerplate.Client.Maui.Platforms.iOS.Services; +//#endif + +namespace Microsoft.Extensions.DependencyInjection; + +public static partial class IIosServiceCollectionExtensions +{ + public static IServiceCollection AddClientMauiProjectIosServices(this IServiceCollection services, IConfiguration configuration) + { + // Services registered in this class can be injected in iOS. + + //#if (notification == true) + services.AddSingleton<IPushNotificationService, iOSPushNotificationService>(); + //#endif + + return services; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 6be4e370d7..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Microsoft.Extensions.DependencyInjection; - -public static partial class IServiceCollectionExtensions -{ - public static IServiceCollection AddClientMauiProjectIosServices(this IServiceCollection services) - { - // Services registered in this class can be injected in iOS. - - return services; - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/NSDataExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/NSDataExtensions.cs new file mode 100644 index 0000000000..53b6e96b7c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Extensions/NSDataExtensions.cs @@ -0,0 +1,21 @@ +using System.Text; + +namespace Foundation; + +public static partial class NSDataExtensions +{ + public static string? ToHexString(this NSData data) + { + var bytes = data.ToArray(); + + if (bytes == null) + return null; + + StringBuilder sb = new StringBuilder(bytes.Length * 2); + + foreach (byte b in bytes) + sb.AppendFormat("{0:x2}", b); + + return sb.ToString().ToUpperInvariant(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Info.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Info.plist index 4b84a0f512..13ab357c0c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Info.plist +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Info.plist @@ -32,5 +32,12 @@ </array> <key>XSAppIconAssets</key> <string>Assets.xcassets/appicon.appiconset</string> + <!--#if (notification == true)--> + <key>UIBackgroundModes</key> + <array> + <string>fetch</string> + <string>remote-notification</string> + </array> + <!--#endif--> </dict> </plist> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/AppUNUserNotificationCenterDelegate.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/AppUNUserNotificationCenterDelegate.cs new file mode 100644 index 0000000000..eb47e4c193 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/AppUNUserNotificationCenterDelegate.cs @@ -0,0 +1,23 @@ +using UserNotifications; + +namespace Boilerplate.Client.Maui.Platforms.iOS.Services; +public partial class AppUNUserNotificationCenterDelegate : UNUserNotificationCenterDelegate +{ + public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) + { + // Runs when user taps on push notification. + // Use the following code to get the action value from the tapped push notification. + // var actionValue = response.Notification.Request.Content.UserInfo.ObjectForKey(new NSString("action")) as NSString; + } + + public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) + { + // Displays the notification when the app is in the foreground. + completionHandler(UNNotificationPresentationOptions.Alert | + UNNotificationPresentationOptions.Badge | + UNNotificationPresentationOptions.Sound); + + // Use the following code to get the action value from the push notification. + // var actionValue = notification.Request.Content.UserInfo.ObjectForKey(new NSString("action")) as NSString; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs new file mode 100644 index 0000000000..123d18e129 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs @@ -0,0 +1,53 @@ +using UIKit; +using Plugin.LocalNotification; +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Maui.Platforms.iOS.Services; + +public partial class iOSPushNotificationService : PushNotificationServiceBase +{ + public async override Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken) + { + return await MainThread.InvokeOnMainThreadAsync(async () => + { + if (await LocalNotificationCenter.Current.AreNotificationsEnabled() is false) + { + await LocalNotificationCenter.Current.RequestNotificationPermission(); + } + + return await LocalNotificationCenter.Current.AreNotificationsEnabled(); + }); + } + + public string GetDeviceId() => UIDevice.CurrentDevice.IdentifierForVendor.ToString(); + + public override async Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) + { + using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15)); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token); + + try + { + while (string.IsNullOrEmpty(Token)) + { + // After the NotificationsSupported Task completes with a result of true, + // we use UNUserNotificationCenter.Current.Delegate. + // This method is asynchronous and we need to wait for it to complete. + await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token); + } + } + catch (Exception exp) + { + throw new InvalidOperationException("Unable to resolve token for APNS.", exp); + } + + var installation = new DeviceInstallationDto + { + InstallationId = GetDeviceId(), + Platform = "apns", + PushChannel = Token + }; + + return installation; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Properties/launchSettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Properties/launchSettings.json index edf8aadcc8..eb8c29d88f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Properties/launchSettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Properties/launchSettings.json @@ -1,8 +1,8 @@ { - "profiles": { - "Windows Machine": { - "commandName": "MsixPackage", - "nativeDebugging": false + "profiles": { + "Windows Machine": { + "commandName": "MsixPackage", + "nativeDebugging": false + } } - } } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiAppInsightsTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiAppInsightsTelemetryInitializer.cs new file mode 100644 index 0000000000..77e772e560 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiAppInsightsTelemetryInitializer.cs @@ -0,0 +1,36 @@ +//+:cnd:noEmit +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.Extensibility; + +namespace Boilerplate.Client.Maui.Services; + +public partial class MauiAppInsightsTelemetryInitializer : ITelemetryInitializer +{ + public void Initialize(ITelemetry telemetry) + { + if (ITelemetryContext.Current is not null) + { + telemetry.Context.Session.Id = ITelemetryContext.Current.AppSessionId.ToString(); + telemetry.Context.Component.Version = ITelemetryContext.Current.AppVersion; + telemetry.Context.Device.OperatingSystem = ITelemetryContext.Current.OS; + telemetry.Context.User.AuthenticatedUserId = ITelemetryContext.Current.UserId?.ToString(); + + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserSessionId)] = ITelemetryContext.Current.UserSessionId?.ToString(); + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.WebView)] = ITelemetryContext.Current.WebView; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserAgent)] = ITelemetryContext.Current.UserAgent; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.TimeZone)] = ITelemetryContext.Current.TimeZone; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.Culture)] = ITelemetryContext.Current.Culture; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.IsOnline)] = ITelemetryContext.Current.IsOnline.ToString().ToLowerInvariant(); + } + + telemetry.Context.Session.IsFirst = VersionTracking.IsFirstLaunchEver; + telemetry.Context.Device.OemName = DeviceInfo.Current.Manufacturer; + telemetry.Context.Device.Model = DeviceInfo.Current.Model; + telemetry.Context.Device.Type = DeviceInfo.Idiom.ToString(); + + if (AppPlatform.IsIosOnMacOS) + { + telemetry.Context.GlobalProperties["IsiOSApplicationOnMac"] = "true"; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiDeviceCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiDeviceCoordinator.cs index 8362af157f..ee9090af41 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiDeviceCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiDeviceCoordinator.cs @@ -8,25 +8,6 @@ namespace Boilerplate.Client.Maui.Services; /// </summary> public partial class MauiDeviceCoordinator : IBitDeviceCoordinator { - public double GetStatusBarHeight() - { -#if Android - var resourceId = MauiApplication.Current.Resources!.GetIdentifier("status_bar_height", "dimen", "android"); - var dimensionPixelSize = MauiApplication.Current.Resources.GetDimensionPixelSize(resourceId); - var density = (double)DeviceDisplay.Current.MainDisplayInfo.Density; - return dimensionPixelSize / density; -#elif iOS - var window = UIKit.UIApplication.SharedApplication.Windows.First().WindowScene; - return window!.StatusBarManager!.StatusBarFrame.Height; -#elif Windows - return 30; -#elif Mac - return 25; -#else - return 0; -#endif - } - public async Task ApplyTheme(bool isDark) { Application.Current!.UserAppTheme = isDark ? AppTheme.Dark : AppTheme.Light; @@ -44,7 +25,7 @@ public async Task ApplyTheme(bool isDark) await Device.InvokeOnMainThreadAsync(() => { UIKit.UIApplication.SharedApplication.SetStatusBarStyle(statusBarStyle, false); - Platform.GetCurrentUIViewController().SetNeedsStatusBarAppearanceUpdate(); + Platform.GetCurrentUIViewController()!.SetNeedsStatusBarAppearanceUpdate(); }); #elif MACCATALYST var window = UIKit.UIApplication.SharedApplication.Windows[0].WindowScene; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs index 8431892056..aba5b8504f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs @@ -11,6 +11,11 @@ public partial class MauiExceptionHandler : ExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary<string, object> parameters) { + exception = UnWrapException(exception); + + if (IgnoreException(exception)) + return; + base.Handle(exception, parameters); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs index f1acca4e2e..79885729bc 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiLocalHttpServer.cs @@ -2,7 +2,8 @@ using System.Net.Sockets; using EmbedIO; using EmbedIO.Actions; -using Boilerplate.Client.Core; +using Boilerplate.Client.Core.Components; +using Microsoft.Extensions.Logging; namespace Boilerplate.Client.Maui.Services; @@ -28,7 +29,7 @@ public int Start(CancellationToken cancellationToken) ctx.Redirect(url); - _ = Routes.OpenUniversalLink(ctx.Request.Url.PathAndQuery, replace: true); + await Routes.OpenUniversalLink(ctx.Request.Url.PathAndQuery, replace: true); } catch (Exception exp) { @@ -36,6 +37,15 @@ public int Start(CancellationToken cancellationToken) } })); + localHttpServer.HandleHttpException(async (context, exception) => + { + exceptionHandler.Handle(new HttpRequestException(exception.Message), new Dictionary<string, object?>() + { + { "StatusCode" , exception.StatusCode }, + { "RequestUri" , context.Request.Url }, + }); + }); + _ = localHttpServer.RunAsync(cancellationToken) .ContinueWith(task => { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryContext.cs new file mode 100644 index 0000000000..a47ca63a89 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryContext.cs @@ -0,0 +1,18 @@ +namespace Boilerplate.Client.Maui.Services; + +public class MauiTelemetryContext : AppTelemetryContext +{ + public override string? OS { get; set; } = $"{DeviceInfo.Current.Manufacturer} {(AppPlatform.IsIosOnMacOS ? DevicePlatform.macOS : DeviceInfo.Current.Platform)} {DeviceInfo.Current.Version}"; + + public override string? AppVersion { get; set; } = VersionTracking.CurrentVersion; + + //-:cnd:noEmit + public override string? WebView { get; set; } = +#if Android + $"{Android.Webkit.WebView.CurrentWebViewPackage?.PackageName} {Android.Webkit.WebView.CurrentWebViewPackage?.VersionName}"; +#elif Windows + $"EdgeWebView2 {Microsoft.Web.WebView2.Core.CoreWebView2Environment.GetAvailableBrowserVersionString()}"; +#else + null; +#endif +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs deleted file mode 100644 index a04759fdb9..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility; - -namespace Boilerplate.Client.Maui.Services; - -public partial class MauiTelemetryInitializer : ITelemetryInitializer -{ - private string sessionId { get; } = Guid.NewGuid().ToString(); - - public void Initialize(ITelemetry telemetry) - { - telemetry.Context.Session.Id = sessionId; - telemetry.Context.Session.IsFirst = VersionTracking.IsFirstLaunchEver; - - telemetry.Context.Device.OperatingSystem = DeviceInfo.Current.Platform.ToString(); - telemetry.Context.Device.OemName = DeviceInfo.Current.Manufacturer; - telemetry.Context.Device.Model = DeviceInfo.Current.Model; - - telemetry.Context.Component.Version = AppInfo.Current.VersionString; - - if (AppPlatform.IsIOS) - { - telemetry.Context.GlobalProperties["IsiOSApplicationOnMac"] = AppPlatform.IsIosOnMacOS.ToString(); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Development.json new file mode 100644 index 0000000000..2b7a979354 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Development.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Production.json new file mode 100644 index 0000000000..2b7a979354 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.Production.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.json new file mode 100644 index 0000000000..d81b30bff0 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/appsettings.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/index.html b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/index.html index a94b77a0ab..d003f4814e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/index.html +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/wwwroot/index.html @@ -4,12 +4,15 @@ <base href="/" /> <meta charset="utf-8" /> <meta name="theme-color"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <!--#if (captcha == "reCaptcha")--> <link rel="preconnect" href="https://www.google.com"> <link rel="preconnect" href="https://www.gstatic.com" crossorigin> <!--#endif--> + <!--#if (appInsights == true)--> + <link rel="preconnect" href="https://js.monitor.azure.com" crossorigin> + <!--#endif--> <link rel="preconnect" href="https://use-your-server-url-here.com"> @@ -25,7 +28,6 @@ <link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <link rel="stylesheet" href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" /> <link rel="stylesheet" href="_content/Boilerplate.Client.Core/styles/app.css" /> - <link rel="stylesheet" href="_content/Boilerplate.Client.Core/Boilerplate.Client.Core.bundle.scp.css" /> <link rel="stylesheet" href="Boilerplate.Client.Maui.styles.css" /> <!--#if (windows == true)--> <link rel="stylesheet" href="Boilerplate.Client.Windows.styles.css" /> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj index a73cc986bf..57509f1fc4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Boilerplate.Client.Web.csproj @@ -12,7 +12,6 @@ <WasmEnableSIMD>false</WasmEnableSIMD> <ServiceWorkerAssetsManifest Condition=" '$(PwaEnabled)' == 'true' ">service-worker-assets.js</ServiceWorkerAssetsManifest> <BlazorCacheBootResources Condition=" '$(PwaEnabled)' == 'true' ">false</BlazorCacheBootResources> - <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile> <StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode> <!--/+:msbuild-conditional:noEmit --> <WasmStripILAfterAOT Condition=" '$(offlineDb)' == 'false'">true</WasmStripILAfterAOT> @@ -21,35 +20,27 @@ </PropertyGroup> <ItemGroup Condition=" '$(PwaEnabled)' == 'true' "> - <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.js" /> - </ItemGroup> - - <ItemGroup Condition=" '$(BlazorWebAssemblyStandalone)' == 'false' "> - <Content Remove="wwwroot\index.html" /> - <None Remove="Properties\launchSettings.json" /> + <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" /> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> - <PackageReference Include="Bit.Bswup" Version="8.11.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" PrivateAssets="all" /> + <PackageReference Include="Bit.Bswup" /> <!--/+:msbuild-conditional:noEmit --> - <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="BlazorApplicationInsights" Version="3.1.0" /> - <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" Version="13.0.3" /> + <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" /> <!--/-:msbuild-conditional:noEmit --> <BlazorWebAssemblyLazyLoad Include="Bit.BlazorUI.Icons.wasm" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Boilerplate.Client.Core\Boilerplate.Client.Core.csproj" /> - + <Content Remove="appsettings*.json" /> + <EmbeddedResource Include="appsettings*.json" /> <Using Include="Microsoft.JSInterop" /> - <Using Include="Boilerplate.Client.Core" /> - <Using Include="Boilerplate.Client.Core.Services.Contracts" /> <Using Include="Boilerplate.Client.Core.Services" /> - <Using Include="Boilerplate.Client.Core.Components.Layout" /> + <Using Include="Boilerplate.Client.Core.Components" /> + <Using Include="Boilerplate.Client.Core.Services.Contracts" /> </ItemGroup> <!--/+:msbuild-conditional:noEmit --> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/ClientWebSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/ClientWebSettings.cs new file mode 100644 index 0000000000..204be99a4c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/ClientWebSettings.cs @@ -0,0 +1,101 @@ +//+:cnd:noEmit +using Boilerplate.Client.Core; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; + +namespace Boilerplate.Client.Web; + +public class ClientWebSettings : ClientCoreSettings +{ + [Required] + public WebAppRenderOptions WebAppRender { get; set; } = default!; + + //#if (notification == true) + public AdsPushVapidOptions? AdsPushVapid { get; set; } + //#endif + + public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = base.Validate(validationContext).ToList(); + + if (WebAppRender is null) + throw new InvalidOperationException("WebAppRender is required. Please set WebAppRender in Client.Core's appsettings.json"); + + Validator.TryValidateObject(WebAppRender, new ValidationContext(WebAppRender), validationResults, true); + + //#if (notification == true) + if (AdsPushVapid is not null) + { + Validator.TryValidateObject(AdsPushVapid, new ValidationContext(AdsPushVapid), validationResults, true); + + if (AppEnvironment.IsDev() is false && AdsPushVapid.PublicKey is "BDSNUvuIISD8NQVByQANEtZ2foKaENIcIGUxsiQs9kDz11fQik8c9WeiMwUHs3iTgNNH4nvXioNQIEsn4OAjTKc") + { + validationResults.Add(new ValidationResult("Please set your own AdsPushVapid.PublicKey in Client.Core's appsettings.json")); + } + } + //#endif + + return validationResults; + } +} + +public partial class WebAppRenderOptions +{ + public bool PrerenderEnabled { get; set; } + + public BlazorWebAppMode BlazorMode { get; set; } + + public IComponentRenderMode? RenderMode + { + get + { + return BlazorMode switch + { + BlazorWebAppMode.BlazorAuto => new InteractiveAutoRenderMode(PrerenderEnabled), + BlazorWebAppMode.BlazorWebAssembly => new InteractiveWebAssemblyRenderMode(PrerenderEnabled), + BlazorWebAppMode.BlazorServer => new InteractiveServerRenderMode(PrerenderEnabled), + BlazorWebAppMode.BlazorSsr => null, + _ => throw new NotImplementedException(), + }; + } + } + + //-:cnd:noEmit + /// <summary> + /// To enable/disable pwa support, navigate to Directory.Build.props and modify the PwaEnabled flag. + /// </summary> + public bool PwaEnabled => +#if PwaEnabled + true; +#else + false; +#endif + //+:cnd:noEmit +} + +/// <summary> +/// https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes#render-modes +/// </summary> +public enum BlazorWebAppMode +{ + BlazorAuto, + BlazorServer, + BlazorWebAssembly, + /// <summary> + /// Pre-rendering without interactivity + /// </summary> + BlazorSsr, +} + +//#if (notification == true) +/// <summary> +/// https://github.com/adessoTurkey-dotNET/AdsPush +/// </summary> +public class AdsPushVapidOptions +{ + /// <summary> + /// Web push's vapid. More info at https://vapidkeys.com/ + /// </summary> + [Required] + public string PublicKey { get; set; } = default!; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Components/AppBswupProgressBar.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Components/AppBswupProgressBar.razor index f66ab0e6e3..ac635f7e00 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Components/AppBswupProgressBar.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Components/AppBswupProgressBar.razor @@ -2,6 +2,8 @@ @using Bit.BlazorUI @using Boilerplate.Client.Core.Components +@* https://bitplatform.dev/bswup/overview *@ + @inherits AppComponentBase <style> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.Services.cs index b8919f8d04..613ad4eb03 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.Services.cs @@ -1,8 +1,4 @@ //+:cnd:noEmit -//#if (appInsights == true) -using BlazorApplicationInsights; -//#endif -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Configuration; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Boilerplate.Client.Web.Services; @@ -13,15 +9,15 @@ public static partial class Program { public static void ConfigureServices(this WebAssemblyHostBuilder builder) { - // Services being registered here can get injected in web project only. - var services = builder.Services; var configuration = builder.Configuration; + // The following services are blazor web assembly only. - configuration.AddClientConfigurations(); - + builder.Logging.ConfigureLoggers(); builder.Logging.AddConfiguration(configuration.GetSection("Logging")); + services.AddClientWebProjectServices(configuration); + Uri.TryCreate(configuration.GetServerAddress(), UriKind.RelativeOrAbsolute, out var serverAddress); if (serverAddress!.IsAbsoluteUri is false) @@ -29,35 +25,28 @@ public static void ConfigureServices(this WebAssemblyHostBuilder builder) serverAddress = new Uri(new Uri(builder.HostEnvironment.BaseAddress), serverAddress); } - services.TryAddSingleton(sp => new HttpClient(sp.GetRequiredKeyedService<DelegatingHandler>("DefaultMessageHandler")) { BaseAddress = serverAddress }); - - //#if (appInsights == true) - services.AddBlazorApplicationInsights(x => - { - x.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"]; - }, - async appInsights => - { - await appInsights.AddTelemetryInitializer(new() - { - Tags = new Dictionary<string, object?>() - { - { "ai.application.ver", typeof(Program).Assembly.GetName().Version!.ToString() } - } - }); - }); - //#endif - - services.AddClientWebProjectServices(); + services.AddScoped(sp => new HttpClient(sp.GetRequiredService<HttpMessageHandler>()) { BaseAddress = serverAddress }); } - public static void AddClientWebProjectServices(this IServiceCollection services) + public static void AddClientWebProjectServices(this IServiceCollection services, IConfiguration configuration) { - // Services being registered here can get injected in both web project and server (during prerendering). + services.AddClientCoreProjectServices(configuration); + // The following services work both in blazor web assembly and server side for pre-rendering and blazor server. + + services.AddTransient<IPrerenderStateService, WebPrerenderStateService>(); + + services.AddScoped<IBitDeviceCoordinator, WebDeviceCoordinator>(); + services.AddScoped<IExceptionHandler, WebExceptionHandler>(); + services.AddScoped<IStorageService, BrowserStorageService>(); + //#if (notification == true) + services.AddScoped<IPushNotificationService, WebPushNotificationService>(); + //#endif - services.TryAddTransient<IBitDeviceCoordinator, WebDeviceCoordinator>(); - services.TryAddTransient<IExceptionHandler, WebExceptionHandler>(); + services.AddSingleton(sp => configuration.Get<ClientWebSettings>()!); - services.AddClientCoreProjectServices(); + services.AddOptions<ClientWebSettings>() + .Bind(configuration) + .ValidateDataAnnotations() + .ValidateOnStart(); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.cs index 89d9206d2f..fea7c32e43 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Program.cs @@ -1,11 +1,8 @@ //-:cnd:noEmit -using System.Web; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Bit.Butil; -#if BlazorWebAssemblyStandalone -using Microsoft.AspNetCore.Components.Web; -#endif namespace Boilerplate.Client.Web; @@ -17,16 +14,20 @@ public static async Task Main(string[] args) AppEnvironment.Set(builder.HostEnvironment.Environment); -#if BlazorWebAssemblyStandalone - builder.RootComponents.Add<Routes>("#app-container"); - builder.RootComponents.Add<HeadOutlet>("head::after"); -#endif + builder.Configuration.AddClientConfigurations(clientEntryAssemblyName: "Boilerplate.Client.Web"); - //+:cnd:noEmit - //#if (appInsights == true) - builder.RootComponents.Add<BlazorApplicationInsights.ApplicationInsightsInit>("head::after"); - //#endif - //-:cnd:noEmit + if (Environment.GetEnvironmentVariable("__BLAZOR_WEBASSEMBLY_WAIT_FOR_ROOT_COMPONENTS") != "true") + { + // By default, App.razor adds Routes and HeadOutlet. + // The following is only required for blazor webassembly standalone. + builder.RootComponents.Add<HeadOutlet>("head::after"); + //+:cnd:noEmit + //#if (appInsights == true) + builder.RootComponents.Add<BlazorApplicationInsights.ApplicationInsightsInit>(selector: "head::after"); + //#endif + //-:cnd:noEmit + builder.RootComponents.Add<Routes>("#app-container"); + } builder.ConfigureServices(); @@ -44,25 +45,13 @@ public static async Task Main(string[] args) var navigationManager = host.Services.GetRequiredService<NavigationManager>(); - var culture = navigationManager.GetCultureFromUri() ?? // 1- Culture query string OR Route data request culture + var culture = new Uri(navigationManager.Uri).GetCulture() ?? // 1- Culture query string OR Route data request culture cultureCookie ?? // 2- User settings CultureInfo.CurrentUICulture.Name; // 3- OS/Browser settings host.Services.GetRequiredService<CultureInfoManager>().SetCurrentCulture(culture); } - try - { - await host.RunAsync(); - } - catch (JSException exp) when (exp.Message is "Error: Could not find any element matching selector '#app-container'.") - { -#if BlazorWebAssemblyStandalone - await System.Console.Error.WriteLineAsync("Either run/publish Client.Web project or set BlazorWebAssemblyStandalone to false."); -#else - throw; -#endif - } - + await host.RunAsync(); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/BrowserStorageService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/BrowserStorageService.cs similarity index 93% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/BrowserStorageService.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/BrowserStorageService.cs index eeab97759d..91a29be8ab 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/BrowserStorageService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/BrowserStorageService.cs @@ -1,4 +1,6 @@ -namespace Boilerplate.Client.Core.Services; +using Bit.Butil; + +namespace Boilerplate.Client.Web.Services; public partial class BrowserStorageService : IStorageService { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs index d0af5c139c..2b6f05695e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs @@ -4,6 +4,11 @@ public partial class WebExceptionHandler : ExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary<string, object> parameters) { + exception = UnWrapException(exception); + + if (IgnoreException(exception)) + return; + base.Handle(exception, parameters); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PrerenderStateService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPrerenderStateService.cs similarity index 59% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PrerenderStateService.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPrerenderStateService.cs index e17c7af9f2..0232dc1144 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PrerenderStateService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPrerenderStateService.cs @@ -1,27 +1,30 @@ //-:cnd:noEmit +using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using Microsoft.AspNetCore.Components; -namespace Boilerplate.Client.Core.Services; +namespace Boilerplate.Client.Web.Services; /// <summary> /// For more information <see cref="IPrerenderStateService"/> docs. /// </summary> -public partial class PrerenderStateService : IPrerenderStateService, IAsyncDisposable +public partial class WebPrerenderStateService : IPrerenderStateService, IAsyncDisposable { private PersistingComponentStateSubscription? subscription; + private readonly ClientWebSettings clientAppSettings = default!; private readonly PersistentComponentState? persistentComponentState; private readonly ConcurrentDictionary<string, object?> values = new(); - private static bool noPersistant = AppRenderMode.Current == AppRenderMode.StaticSsr || - AppRenderMode.PrerenderEnabled is false || - AppPlatform.IsBlazorHybrid; + private bool NoPersistent => clientAppSettings.WebAppRender.RenderMode is null /*Ssr*/ || + clientAppSettings.WebAppRender.PrerenderEnabled is false; - public PrerenderStateService(PersistentComponentState? persistentComponentState = null) + public WebPrerenderStateService(ClientWebSettings clientWebSettings, PersistentComponentState? persistentComponentState = null) { - if (noPersistant) return; - subscription = persistentComponentState?.RegisterOnPersisting(PersistAsJson, AppRenderMode.Current); + this.clientAppSettings = clientWebSettings; this.persistentComponentState = persistentComponentState; + if (NoPersistent) return; + subscription = persistentComponentState?.RegisterOnPersisting(PersistAsJson, clientWebSettings.WebAppRender.RenderMode); } public async Task<T?> GetValue<T>(Func<Task<T?>> factory, @@ -29,7 +32,7 @@ public PrerenderStateService(PersistentComponentState? persistentComponentState [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "") { - if (noPersistant) return await factory(); + if (NoPersistent) return await factory(); string key = $"{filePath.Split('\\').LastOrDefault()} {memberName} {lineNumber}"; @@ -38,7 +41,7 @@ public PrerenderStateService(PersistentComponentState? persistentComponentState public async Task<T?> GetValue<T>(string key, Func<Task<T?>> factory) { - if (noPersistant) return await factory(); + if (NoPersistent) return await factory(); if (persistentComponentState!.TryTakeFromJson(key, out T? value)) return value; @@ -49,10 +52,9 @@ public PrerenderStateService(PersistentComponentState? persistentComponentState void Persist<T>(string key, T value) { - if (noPersistant) return; + if (NoPersistent || AppPlatform.IsBrowser) return; - values.TryRemove(key, out object? _); - values.TryAdd(key, value); + values.AddOrUpdate(key, value, (_, _) => value); } async Task PersistAsJson() @@ -65,7 +67,7 @@ async Task PersistAsJson() public async ValueTask DisposeAsync() { - if (noPersistant) return; + if (NoPersistent) return; subscription?.Dispose(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPushNotificationService.cs new file mode 100644 index 0000000000..708b522bbb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebPushNotificationService.cs @@ -0,0 +1,19 @@ +using Bit.Butil; +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Web.Services; + +public partial class WebPushNotificationService : PushNotificationServiceBase +{ + [AutoInject] private Notification notification = default!; + [AutoInject] private readonly IJSRuntime jSRuntime = default!; + [AutoInject] private readonly ClientWebSettings clientWebSettings = default!; + + public async override Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) + { + return await jSRuntime.GetDeviceInstallation(clientWebSettings.AdsPushVapid!.PublicKey); + } + + public override async Task<bool> IsPushNotificationSupported(CancellationToken cancellationToken) => clientWebSettings.WebAppRender.PwaEnabled + && string.IsNullOrEmpty(clientWebSettings.AdsPushVapid?.PublicKey) is false && await notification.IsNotificationAvailable(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Development.json new file mode 100644 index 0000000000..2b7a979354 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Development.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Production.json new file mode 100644 index 0000000000..a21d17f43b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.Production.json @@ -0,0 +1,6 @@ +{ + "WebAppRender": { + "BlazorMode": "BlazorAuto" + }, + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json new file mode 100644 index 0000000000..5fe62e7f93 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json @@ -0,0 +1,15 @@ +{ + "WebAppRender": { + "PrerenderEnabled": false, + "BlazorMode": "BlazorServer", + "BlazorMode_Comment": "BlazorServer, BlazorWebAssembly and BlazorAuto. Default value of Client.Core/appsettings.Production.json is BlazorAuto" + }, + //#if (notification == true) + "AdsPushVapid_Comment": "https://github.com/adessoTurkey-dotNET/AdsPush", + "AdsPushVapid": { + "AdsPushVapid_Comment": "Web push's vapid. More info at https://vapidkeys.com/", + "PublicKey": "BDSNUvuIISD8NQVByQANEtZ2foKaENIcIGUxsiQs9kDz11fQik8c9WeiMwUHs3iTgNNH4nvXioNQIEsn4OAjTKc" + }, + //#endif + "$schema": "https://json.schemastore.org/appsettings.json" +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/apple-app-site-association b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/apple-app-site-association index c3c75bd337..479c377c60 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/apple-app-site-association +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/.well-known/apple-app-site-association @@ -8,32 +8,32 @@ { "appID": "76WD644YU8.com.bitplatform.AdminPanel.Template", "paths": [ - "NOT /api*", - "NOT /odata*", - "NOT /jobs*", - "NOT /core*", - "NOT /signalr*", - "NOT /healthchecks-ui*", - "NOT /healthz*", - "NOT /swagger*", - "NOT /signin*", - "NOT /.well-known*", + "NOT /api/*", + "NOT /odata/*", + "NOT /jobs/*", + "NOT /core/*", + "NOT /signalr/*", + "NOT /healthchecks-ui/*", + "NOT /healthz/*", + "NOT /swagger/*", + "NOT /signin/*", + "NOT /.well-known/*", "*" ] }, { "appID": "76WD644YU8.com.bitplatform.Todo.Template", "paths": [ - "NOT /api*", - "NOT /odata*", - "NOT /jobs*", - "NOT /core*", - "NOT /signalr*", - "NOT /healthchecks-ui*", - "NOT /healthz*", - "NOT /swagger*", - "NOT /signin*", - "NOT /.well-known*", + "NOT /api/*", + "NOT /odata/*", + "NOT /jobs/*", + "NOT /core/*", + "NOT /signalr/*", + "NOT /healthchecks-ui/*", + "NOT /healthz/*", + "NOT /swagger/*", + "NOT /signin/*", + "NOT /.well-known/*", "*" ] } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/index.html b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/index.html index bf34de5e76..32fcbcef0d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/index.html +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/index.html @@ -4,19 +4,23 @@ <base href="/"> <meta charset="utf-8"> <meta name="theme-color"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="description" content="The Boilerplate is built with ASP.NET Core, Identity, Web API, EF Core and Blazor."> <script> // disable auto-zoom of iOS Safari when focusing an input (/iPad|iPhone|iPod/.test(navigator.userAgent)) && - (document.querySelector('meta[name="viewport"]').content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0') + (document.querySelector('meta[name="viewport"]').content = 'width=device-width, initial-scale=1.0, viewport-fit=cover, maximum-scale=1.0') </script> <!--#if (captcha == "reCaptcha")--> <link rel="preconnect" href="https://www.google.com"> <link rel="preconnect" href="https://www.gstatic.com" crossorigin> <!--#endif--> + <!--#if (appInsights == true)--> + <link rel="preconnect" href="https://js.monitor.azure.com" crossorigin> + <!--#endif--> + <link rel="preconnect" href="https://use-your-server-url-here.com"> @@ -30,7 +34,7 @@ <link href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" rel="stylesheet" /> <link href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" rel="stylesheet" /> <link href="_content/Boilerplate.Client.Core/styles/app.css" rel="stylesheet" /> - <link href="_content/Boilerplate.Client.Core/Boilerplate.Client.Core.bundle.scp.css" rel="stylesheet" /> + <link href="Boilerplate.Client.Web.styles.css" rel="stylesheet" /> <script src="_framework/blazor.webassembly.js" autostart="false"></script> <script src="_content/Bit.Bswup/bit-bswup.js" blazorScript="_framework/blazor.webassembly.js"></script> <script src="_content/Bit.Bswup/bit-bswup.progress.js"></script> @@ -185,25 +189,23 @@ } } </style> - <div> - <div id="app-container"> - <div class="bit-lds-wrapper"> - <div class="bit-lds-grid"> - <div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div> - </div> - </div> - <div id="bit-bswup"> - <div class="bswup-progress-text"></div> - <svg class="bswup-container"> - <circle r="40%" cx="50%" cy="50%"></circle> - <circle r="40%" cx="50%" cy="50%"></circle> - </svg> + <div id="app-container"> + <div class="bit-lds-wrapper"> + <div class="bit-lds-grid"> + <div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div> </div> </div> - <!-- If you want the update to be applied after the user approves the new Pwa version and not automatically, - set autoReload parameter of startBswupProgress to false and uncomment the bit-bswup-reload button code. --> - <!-- <button id="bit-bswup-reload">Update</button> --> + <div id="bit-bswup"> + <div class="bswup-progress-text"></div> + <svg class="bswup-container"> + <circle r="40%" cx="50%" cy="50%"></circle> + <circle r="40%" cx="50%" cy="50%"></circle> + </svg> + </div> </div> + <!-- If you want the update to be applied after the user approves the new Pwa version and not automatically, + set autoReload parameter of startBswupProgress to false and uncomment the bit-bswup-reload button code. --> + <!-- <button id="bit-bswup-reload">Update</button> --> <script> const autoReload = true, showLogs = false, showAssets = false, appContainer = '#app-container', hideApp = false, handler = null; startBswupProgress(autoReload, showLogs, showAssets, appContainer, hideApp, handler); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json index 43292d97a5..50ce5ccb71 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json @@ -39,7 +39,7 @@ "name": "Todo", "url": "/todo" }, - //#elif (sample == "AdminPanel") + //#elif (sample == "Admin") { "name": "Categories", "url": "/categories" diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.js index 3d05c5c721..d8a7fb5f70 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.js +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.js @@ -1,42 +1,21 @@ -// bit version: 8.11.0 -// https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup +//+:cnd:noEmit +// In development, always fetch from the network and do not enable offline support. +// This is because caching would make development more difficult (changes would not +// be reflected on the first load after each change). +self.addEventListener('fetch', () => { }); -self.assetsInclude = []; -self.assetsExclude = [ - /bit\.blazorui\.fluent\.css$/, - /bit\.blazorui\.fluent-dark\.css$/, - /bit\.blazorui\.fluent-light\.css$/, - /Client\.Web\.styles\.css$/ // In .NET 8, an inexistent CSS file is inadvertently included in the assets list under the name 'Boilerplate.Client.Web.styles.css.' - // Subsequently, during the download process of assets list files, bswup attempts to retrieve this non - existent CSS file along with others. - // It is imperative that we expunge this file from the assets list. -]; -self.externalAssets = [ - { - "url": "/" - }, - { - url: "_framework/blazor.web.js?v=8.0.8" - } -]; +//#if (notification == true) +self.addEventListener('push', function (event) { -self.serverHandledUrls = [ - /\/api\//, - /\/odata\//, - /\/jobs\//, - /\/core\//, - /\/signalr\//, - /\/healthchecks-ui/, - /\/healthz/, - /\/swagger/, - /\/signin-/, - /\/.well-known/, - /\/sitemap.xml/, -]; + const data = event.data.json(); -self.defaultUrl = "/"; -self.caseInsensitiveUrl = true; -self.noPrerenderQuery = 'no-prerender=true'; -self.isPassive = self.disablePassiveFirstBoot = true; -self.errorTolerance = 'lax'; + self.registration.showNotification(data.title, { -self.importScripts('_content/Bit.Bswup/bit-bswup.sw.js'); \ No newline at end of file + body: data.message, + + icon: '/images/icons/bit-icon-512.png' + + }); + +}); +//#endif \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js new file mode 100644 index 0000000000..e275214ce2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js @@ -0,0 +1,60 @@ +//+:cnd:noEmit +// bit version: 8.12.0 +// https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup + +//#if (notification == true) + +self.addEventListener('push', function (event) { + + const data = event.data.json(); + + self.registration.showNotification(data.title, { + + body: data.message, + + icon: '/images/icons/bit-icon-512.png' + + }); + +}); + +//#endif + +self.assetsInclude = []; +self.assetsExclude = [ + /bit\.blazorui\.fluent\.css$/, + /bit\.blazorui\.fluent-dark\.css$/, + /bit\.blazorui\.fluent-light\.css$/ +]; +self.externalAssets = [ + { + "url": "/" + }, + { + url: "_framework/blazor.web.js?ver=8.0.404" + }, + { + "url": "Boilerplate.Server.Web.styles.css" + } +]; + +self.serverHandledUrls = [ + /\/api\//, + /\/odata\//, + /\/jobs\//, + /\/core\//, + /\/healthchecks-ui/, + /\/healthz/, + /\/swagger/, + /\/signin-/, + /\/.well-known/, + /\/sitemap.xml/, +]; + +self.defaultUrl = "/"; +self.caseInsensitiveUrl = true; +self.noPrerenderQuery = 'no-prerender=true'; +self.isPassive = self.disablePassiveFirstBoot = true; +self.errorTolerance = 'lax'; + +self.importScripts('_content/Bit.Bswup/bit-bswup.sw.js'); \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/.config/dotnet-tools.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/.config/dotnet-tools.json index 6031cf8fe5..f8cf15add4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/.config/dotnet-tools.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "vpk": { - "version": "0.0.594", + "version": "0.0.869", "commands": [ "vpk" ] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/App.xaml.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/App.xaml.cs index c63e237446..f3a417e7b8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/App.xaml.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/App.xaml.cs @@ -1,7 +1,6 @@ using System.IO.IsolatedStorage; using System.IO; using System.Windows; -using System.Text.Json; using System.Collections; using Microsoft.Win32; using System.Windows.Media; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Boilerplate.Client.Windows.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Boilerplate.Client.Windows.csproj index 9c7518f322..f0d7956536 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Boilerplate.Client.Windows.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Boilerplate.Client.Windows.csproj @@ -19,27 +19,24 @@ <ItemGroup> <Using Include="Boilerplate.Client.Core.Components.Layout" /> + <Using Include="Boilerplate.Client.Core.Components.Pages" /> <Using Include="Boilerplate.Client.Core.Services.Contracts" /> <Using Include="Boilerplate.Client.Core.Services" /> <Using Include="Boilerplate.Shared" /> <Watch Remove="*.scss" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="EmbedIO" Version="3.5.2" /> - <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.EventSource" Version="8.0.0" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="8.0.82" /> - <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2739.15" /> - <PackageReference Include="Velopack" Version="0.0.594" /> + <PackageReference Include="EmbedIO" /> + <PackageReference Include="Microsoft.Extensions.Logging.EventLog" /> + <PackageReference Include="Microsoft.Extensions.Logging.EventSource" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" /> + <PackageReference Include="Microsoft.Web.WebView2" /> + <PackageReference Include="Velopack" /> <!--/+:msbuild-conditional:noEmit --> - <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Analytics" Version="5.0.5" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Crashes" Version="5.0.5" /> - <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="West.Extensions.Logging.AppCenter" Version="2.2.2" /> - <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.22.0" /> + <PackageReference Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Analytics" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Crashes" /> + <PackageReference Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="West.Extensions.Logging.AppCenter" /> + <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.Extensions.Logging.ApplicationInsights" /> <!--/-:msbuild-conditional:noEmit --> <Content Include="..\Boilerplate.Client.Maui\wwwroot\index.html" Link="wwwroot\index.html"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> @@ -55,6 +52,8 @@ <ItemGroup> <Resource Include="Resources\**\*.*" /> + <Content Remove="appsettings*.json" /> + <EmbeddedResource Include="appsettings*.json" /> </ItemGroup> <Target Name="BeforeBuildTasks" AfterTargets="CoreCompile"> @@ -62,7 +61,7 @@ </Target> <Target Name="BuildCssFiles"> - <Exec Command="../Boilerplate.Client.Core/node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="../Boilerplate.Client.Core/node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> </Project> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/ClientWindowsSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/ClientWindowsSettings.cs new file mode 100644 index 0000000000..2ccd1cc977 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/ClientWindowsSettings.cs @@ -0,0 +1,28 @@ +//+:cnd:noEmit +using Boilerplate.Client.Core; + +namespace Boilerplate.Client.Windows; + +public class ClientWindowsSettings : ClientCoreSettings +{ + public WindowsUpdateOptions? WindowsUpdate { get; set; } + + public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = base.Validate(validationContext).ToList(); + + if (WindowsUpdate is not null) + { + Validator.TryValidateObject(WindowsUpdate, new ValidationContext(WindowsUpdate), validationResults, true); + } + + return validationResults; + } +} + +public partial class WindowsUpdateOptions +{ + public bool AutoReload { get; set; } + + public string? FilesUrl { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor index dc7f3e36bf..e44d576374 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor @@ -1,40 +1,23 @@ @attribute [Route(Urls.AboutPage)] @attribute [Route("{culture?}" + Urls.AboutPage)] -@inherits AppComponentBase +@inherits AppPageBase -<div class="page-container"> - <section class="page-section about-section"> - <h1 class="about-section-title">About</h1> - - <div class="about-section-desc"> +<section> + <BitStack Gap="3rem"> + <BitText Typography="BitTypography.Body1"> For Razor pages that are exclusively dependent on native Maui features for Android, iOS, Windows, and macOS functionality, - <br /> consider using Client/Maui project instead of placing them in Client/Core project. <br /> This approach allows direct access to native features without the need for dependency injection (DI) or publish-subscribe messaging patterns. - </div> + </BitText> - <BitStack VerticalAlign="BitAlignment.Center" HorizontalAlign="BitAlignment.SpaceAround"> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>App Name:</BitLabel> - <BitLabel>@appName</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>App Version:</BitLabel> - <BitLabel>@appVersion</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>OS:</BitLabel> - <BitLabel>@os</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>Environment:</BitLabel> - <BitLabel>@AppEnvironment.Current</BitLabel> - </BitStack> - <BitStack Horizontal Gap=".5rem"> - <BitLabel>Process Id:</BitLabel> - <BitLabel>@processId</BitLabel> - </BitStack> + <BitStack AutoWidth> + <BitText>App Name: <b>@appName</b></BitText> + <BitText>App Version: <b>@appVersion</b></BitText> + <BitText>OS: <b>@os</b></BitText> + <BitText>Web View: <b>@webView</b></BitText> + <BitText>Environment: <b>@AppEnvironment.Current</b></BitText> + <BitText>Process Id: <b>@processId</b></BitText> </BitStack> - </section> -</div> \ No newline at end of file + </BitStack> +</section> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.cs index 8dc779bdb2..23e11134a1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.cs @@ -1,21 +1,29 @@ using System.Reflection; -using System.Runtime.InteropServices; namespace Boilerplate.Client.Windows.Components.Pages; public partial class AboutPage { + [AutoInject] private ITelemetryContext telemetryContext = default!; + + protected override string? Title => Localizer[nameof(AppStrings.AboutTitle)]; + protected override string? Subtitle => string.Empty; + + private string appName = default!; private string appVersion = default!; private string os = default!; + private string webView = default!; private string processId = default!; - protected async override Task OnInitAsync() + + protected override async Task OnInitAsync() { var asm = typeof(AboutPage).Assembly; appName = asm.GetCustomAttribute<AssemblyTitleAttribute>()!.Title; - appVersion = asm.GetName().Version!.ToString(); - os = RuntimeInformation.OSDescription; + appVersion = telemetryContext.AppVersion!; + os = telemetryContext.OS!; + webView = telemetryContext.WebView!; processId = Environment.ProcessId.ToString(); await base.OnInitAsync(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.scss index fa443148f8..07c3d06a8b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Components/Pages/AboutPage.razor.scss @@ -1,86 +1,5 @@ -@import "../../../Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss"; +//@import "../../../Boilerplate.Client.Core/Styles/abstracts/_media-queries.scss"; -.page-container { - width: 100%; - display: flex; - align-items: center; - padding: 0 rem2(16px); - flex-flow: column nowrap; - justify-content: flex-start; -} - -::deep * { - direction: ltr; -} - -.page-section { - width: 100%; -} - -.about-section { - width: 100%; - display: flex; - align-items: center; - padding: rem2(144px) 0; - flex-flow: column nowrap; - justify-content: flex-start; - - @include lt-xl { - padding: rem2(130px) 0; - } - - @include sm { - padding: rem2(120px) 0; - } -} - -.about-section-title { - font-weight: bold; - text-align: center; - font-size: rem2(42px); - line-height: rem2(62px); - margin-bottom: rem2(32px); - - @include lg { - font-size: rem2(34px); - line-height: rem2(56px); - margin-bottom: rem2(24px); - } - - @include md { - font-size: rem2(26px); - line-height: rem2(44px); - margin-bottom: rem2(24px); - } - - @include sm { - font-size: rem2(24px); - line-height: rem2(40px); - margin-bottom: rem2(16px); - } -} - -.about-section-desc { - font-size: rem2(16px); - max-width: rem2(1232px); - line-height: rem2(40px); - margin-bottom: rem2(40px); - - @include lg { - max-width: rem2(821px); - line-height: rem2(32px); - margin-bottom: rem2(38px); - } - - @include md { - max-width: rem2(572px); - line-height: rem2(28px); - margin-bottom: rem2(32px); - } - - @include sm { - max-width: 100%; - line-height: rem2(24px); - margin-bottom: rem2(32px); - } +section { + padding: 1rem; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Configuration/WindowsUpdateSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Configuration/WindowsUpdateSettings.cs deleted file mode 100644 index f7c46a22b4..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Configuration/WindowsUpdateSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Boilerplate.Client.Windows.Configuration; - -public partial class WindowsUpdateSettings -{ - public bool AutoReload { get; set; } - - public string? FilesUrl { get; set; } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml index e8438e3504..f5dd3dfe3f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml @@ -3,21 +3,20 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:blazor="http://schemas.microsoft.com/winfx/2006/xaml/presentation/blazor" - xmlns:core="clr-namespace:Boilerplate.Client.Core;assembly=Boilerplate.Client.Core" + xmlns:core="clr-namespace:Boilerplate.Client.Core.Components;assembly=Boilerplate.Client.Core" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="Boilerplate" Background="{DynamicResource PrimaryBgColor}" WindowState="Maximized" mc:Ignorable="d"> - <Grid> - <blazor:BlazorWebView + <blazor:BlazorWebView x:Name="AppWebView" x:FieldModifier="public" - HostPage="wwwroot\index.html"> - <blazor:BlazorWebView.RootComponents> - <blazor:RootComponent ComponentType="{x:Type core:Routes}" Selector="#app-container" /> - </blazor:BlazorWebView.RootComponents> - </blazor:BlazorWebView> - </Grid> + HostPage="wwwroot\index.html" + BlazorWebViewInitializing="BlazorWebViewInitializing"> + <blazor:BlazorWebView.RootComponents> + <blazor:RootComponent ComponentType="{x:Type core:Routes}" Selector="#app-container" /> + </blazor:BlazorWebView.RootComponents> + </blazor:BlazorWebView> </Window> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml.cs index 7a336a070b..b171fb6cbf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/MainWindow.xaml.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Components.WebView.Wpf; +//+:cnd:noEmit +using Microsoft.Web.WebView2.Core; +using Microsoft.AspNetCore.Components.WebView; namespace Boilerplate.Client.Windows; @@ -6,34 +8,59 @@ public partial class MainWindow { public MainWindow() { - AppPlatform.IsBlazorHybrid = true; var services = new ServiceCollection(); - services.ConfigureServices(); + ConfigurationBuilder configurationBuilder = new(); + configurationBuilder.AddClientConfigurations(clientEntryAssemblyName: "Boilerplate.Client.Windows"); + var configuration = configurationBuilder.Build(); + services.AddClientWindowsProjectServices(configuration); InitializeComponent(); + //#if (appInsights == true) + AppWebView.RootComponents.Insert(0, new() + { + ComponentType = typeof(BlazorApplicationInsights.ApplicationInsightsInit), + Selector = "head::after" + }); + //#endif AppWebView.Services = services.BuildServiceProvider(); if (CultureInfoManager.MultilingualEnabled) { - AppWebView.Services.GetRequiredService<CultureInfoManager>().SetCurrentCulture(App.Current.Properties["Culture"]?.ToString() ?? CultureInfo.CurrentUICulture.Name); + AppWebView.Services.GetRequiredService<CultureInfoManager>().SetCurrentCulture( + App.Current.Properties["Culture"]?.ToString() ?? // 1- User settings + CultureInfo.CurrentUICulture.Name); // 2- OS Settings } - AppWebView.Services.GetRequiredService<IPubSubService>().Subscribe(PubSubMessages.CULTURE_CHANGED, async culture => + AppWebView.Services.GetRequiredService<PubSubService>().Subscribe(ClientPubSubMessages.CULTURE_CHANGED, async culture => { App.Current.Shutdown(); Application.Restart(); }); AppWebView.Loaded += async delegate { - AppWebView.WebView.DefaultBackgroundColor = ColorTranslator.FromHtml(Background.ToString()); + AppWebView.WebView.DefaultBackgroundColor = ColorTranslator.FromHtml(App.Current.Resources["PrimaryBgColor"].ToString()!); await AppWebView.WebView.EnsureCoreWebView2Async(); + AppWebView.WebView.CoreWebView2.PermissionRequested += async (sender, args) => + { + args.Handled = true; + args.State = CoreWebView2PermissionState.Allow; + }; var settings = AppWebView.WebView.CoreWebView2.Settings; if (AppEnvironment.IsDev() is false) { settings.IsZoomControlEnabled = false; settings.AreBrowserAcceleratorKeysEnabled = false; } + bool hasBlazorStarted = false; AppWebView.WebView.NavigationCompleted += async delegate { + if (hasBlazorStarted) + return; + hasBlazorStarted = true; await AppWebView.WebView.ExecuteScriptAsync("Blazor.start()"); }; }; } + + void BlazorWebViewInitializing(object sender, BlazorWebViewInitializingEventArgs e) + { + e.EnvironmentOptions = new() { AdditionalBrowserArguments = "--unsafely-treat-insecure-origin-as-secure=https://0.0.0.0 --enable-notifications" }; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.Services.cs index ecabf71916..0a9b133850 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.Services.cs @@ -2,24 +2,21 @@ using System.Net.Http; using Microsoft.Extensions.Logging; using Boilerplate.Client.Windows.Services; -using Boilerplate.Client.Windows.Configuration; namespace Boilerplate.Client.Windows; public static partial class Program { - public static void ConfigureServices(this IServiceCollection services) + public static void AddClientWindowsProjectServices(this IServiceCollection services, IConfiguration configuration) { // Services being registered here can get injected in windows project only. + services.AddClientCoreProjectServices(configuration); - ConfigurationBuilder configurationBuilder = new(); - configurationBuilder.AddClientConfigurations(); - var configuration = configurationBuilder.Build(); - services.TryAddTransient<IConfiguration>(sp => configuration); - - services.TryAddSingleton(sp => + services.AddScoped<IExceptionHandler, WindowsExceptionHandler>(); + services.AddScoped<IBitDeviceCoordinator, WindowsDeviceCoordinator>(); + services.AddScoped(sp => { - var handler = sp.GetRequiredKeyedService<DelegatingHandler>("DefaultMessageHandler"); + var handler = sp.GetRequiredService<HttpMessageHandler>(); HttpClient httpClient = new(handler) { BaseAddress = new Uri(configuration.GetServerAddress(), UriKind.Absolute) @@ -27,38 +24,39 @@ public static void ConfigureServices(this IServiceCollection services) return httpClient; }); - services.AddWpfBlazorWebView(); - if (AppEnvironment.IsDev()) - { - services.AddBlazorWebViewDeveloperTools(); - } - - services.TryAddTransient<IStorageService, WindowsStorageService>(); - services.TryAddTransient<IBitDeviceCoordinator, WindowsDeviceCoordinator>(); - services.TryAddTransient<IExceptionHandler, WindowsExceptionHandler>(); + services.AddSingleton(sp => configuration); + services.AddSingleton<IStorageService, WindowsStorageService>(); services.AddSingleton<ILocalHttpServer, WindowsLocalHttpServer>(); + services.AddSingleton(sp => configuration.Get<ClientWindowsSettings>()!); + services.AddSingleton(ITelemetryContext.Current!); + //#if (notification == true) + services.AddSingleton<IPushNotificationService, WindowsPushNotificationService>(); + //#endif + + services.AddWpfBlazorWebView(); + services.AddBlazorWebViewDeveloperTools(); services.AddLogging(loggingBuilder => { + loggingBuilder.ConfigureLoggers(); loggingBuilder.AddConfiguration(configuration.GetSection("Logging")); - loggingBuilder.AddEventLog(); loggingBuilder.AddEventSourceLogger(); - if (AppEnvironment.IsDev()) + + if (AppPlatform.IsWindows) { - loggingBuilder.AddDebug(); + loggingBuilder.AddEventLog(); } - loggingBuilder.AddConsole(); //#if (appCenter == true) if (Microsoft.AppCenter.AppCenter.Configured) { - loggingBuilder.AddAppCenter(options => { }); + loggingBuilder.AddAppCenter(options => options.IncludeScopes = true); } //#endif //#if (appInsights == true) loggingBuilder.AddApplicationInsights(config => { - config.TelemetryInitializers.Add(new WindowsTelemetryInitializer()); - var connectionString = configuration["ApplicationInsights:ConnectionString"]; + config.TelemetryInitializers.Add(new WindowsAppInsightsTelemetryInitializer()); + var connectionString = configuration.Get<ClientWindowsSettings>()!.ApplicationInsights?.ConnectionString; if (string.IsNullOrEmpty(connectionString) is false) { config.ConnectionString = connectionString; @@ -70,10 +68,9 @@ public static void ConfigureServices(this IServiceCollection services) //#endif }); - services.AddOptions<WindowsUpdateSettings>() - .Bind(configuration.GetRequiredSection(nameof(WindowsUpdateSettings))) + services.AddOptions<ClientWindowsSettings>() + .Bind(configuration) + .ValidateDataAnnotations() .ValidateOnStart(); - - services.AddClientCoreProjectServices(); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.cs index c70eb840a3..f29d6a50d7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Program.cs @@ -1,6 +1,5 @@ -using Boilerplate.Client.Windows.Configuration; -using Microsoft.Extensions.Options; -using Velopack; +using Velopack; +using Boilerplate.Client.Windows.Services; namespace Boilerplate.Client.Windows; @@ -19,6 +18,9 @@ public static void Main(string[] args) //#endif //-:cnd:noEmit + AppPlatform.IsBlazorHybrid = true; + ITelemetryContext.Current = new WindowsTelemetryContext(); + // https://github.com/velopack/velopack VelopackApp.Build().Run(); var application = new App(); @@ -27,7 +29,7 @@ public static void Main(string[] args) var services = await App.Current.Dispatcher.InvokeAsync(() => ((MainWindow)App.Current.MainWindow).AppWebView.Services); try { - var windowsUpdateSettings = services.GetRequiredService<IOptionsSnapshot<WindowsUpdateSettings>>().Value; + var windowsUpdateSettings = services.GetRequiredService<ClientWindowsSettings>().WindowsUpdate; if (string.IsNullOrEmpty(windowsUpdateSettings?.FilesUrl)) { return; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsAppInsightsTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsAppInsightsTelemetryInitializer.cs new file mode 100644 index 0000000000..3589b903d5 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsAppInsightsTelemetryInitializer.cs @@ -0,0 +1,26 @@ +//+:cnd:noEmit +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.Extensibility; + +namespace Boilerplate.Client.Windows.Services; + +public partial class WindowsAppInsightsTelemetryInitializer : ITelemetryInitializer +{ + public void Initialize(ITelemetry telemetry) + { + if (ITelemetryContext.Current is not null) + { + telemetry.Context.Session.Id = ITelemetryContext.Current.AppSessionId.ToString(); + telemetry.Context.Component.Version = ITelemetryContext.Current.AppVersion; + telemetry.Context.Device.OperatingSystem = ITelemetryContext.Current.OS; + telemetry.Context.User.AuthenticatedUserId = ITelemetryContext.Current.UserId?.ToString(); + + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserSessionId)] = ITelemetryContext.Current.UserSessionId?.ToString(); + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.WebView)] = ITelemetryContext.Current.WebView; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserAgent)] = ITelemetryContext.Current.UserAgent; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.TimeZone)] = ITelemetryContext.Current.TimeZone; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.Culture)] = ITelemetryContext.Current.Culture; + telemetry.Context.GlobalProperties[nameof(ITelemetryContext.IsOnline)] = ITelemetryContext.Current.IsOnline.ToString().ToLowerInvariant(); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsDeviceCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsDeviceCoordinator.cs index 0f82bf2694..6dbbf26ea7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsDeviceCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsDeviceCoordinator.cs @@ -1,5 +1,3 @@ -using Boilerplate.Client.Core.Services.Contracts; - -namespace Boilerplate.Client.Windows.Services; +namespace Boilerplate.Client.Windows.Services; public partial class WindowsDeviceCoordinator : IBitDeviceCoordinator { } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs index 333ea29751..03c98a0444 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs @@ -10,6 +10,11 @@ public partial class WindowsExceptionHandler : ExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary<string, object> parameters) { + exception = UnWrapException(exception); + + if (IgnoreException(exception)) + return; + base.Handle(exception, parameters); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs index b9c72520be..5b5c46bb18 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsLocalHttpServer.cs @@ -1,8 +1,10 @@ using System.Net; +using System.Net.Http; using System.Net.Sockets; using EmbedIO; using EmbedIO.Actions; -using Boilerplate.Client.Core; +using Boilerplate.Client.Core.Components; +using Microsoft.Extensions.Logging; namespace Boilerplate.Client.Windows.Services; @@ -10,6 +12,7 @@ public partial class WindowsLocalHttpServer : ILocalHttpServer { [AutoInject] private IConfiguration configuration; [AutoInject] private IExceptionHandler exceptionHandler; + [AutoInject] private ILogger<ILocalHttpServer> logger = default!; private WebServer? localHttpServer; @@ -28,7 +31,9 @@ public int Start(CancellationToken cancellationToken) ctx.Redirect(url); - _ = Routes.OpenUniversalLink(ctx.Request.Url.PathAndQuery, replace: true); + await App.Current.Dispatcher.InvokeAsync(() => App.Current.MainWindow.Activate()); + + await Routes.OpenUniversalLink(ctx.Request.Url.PathAndQuery, replace: true); } catch (Exception exp) { @@ -36,6 +41,15 @@ public int Start(CancellationToken cancellationToken) } })); + localHttpServer.HandleHttpException(async (context, exception) => + { + exceptionHandler.Handle(new HttpRequestException(exception.Message), new Dictionary<string, object?>() + { + { "StatusCode" , exception.StatusCode }, + { "RequestUri" , context.Request.Url }, + }); + }); + _ = localHttpServer.RunAsync(cancellationToken) .ContinueWith(task => { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsPushNotificationService.cs new file mode 100644 index 0000000000..c28d3d98a2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsPushNotificationService.cs @@ -0,0 +1,9 @@ +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Client.Windows.Services; + +public partial class WindowsPushNotificationService : PushNotificationServiceBase +{ + public override Task<DeviceInstallationDto> GetDeviceInstallation(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryContext.cs new file mode 100644 index 0000000000..247e2119b4 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryContext.cs @@ -0,0 +1,6 @@ +namespace Boilerplate.Client.Windows.Services; + +public class WindowsTelemetryContext : AppTelemetryContext +{ + public override string? WebView { get; set; } = $"EdgeWebView2 {Microsoft.Web.WebView2.Core.CoreWebView2Environment.GetAvailableBrowserVersionString()}"; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs deleted file mode 100644 index 0117cba282..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Runtime.InteropServices; -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility; - -namespace Boilerplate.Client.Windows.Services; - -public partial class WindowsTelemetryInitializer : ITelemetryInitializer -{ - private string sessionId { get; } = Guid.NewGuid().ToString(); - - public void Initialize(ITelemetry telemetry) - { - telemetry.Context.Session.Id = sessionId; - - telemetry.Context.Device.OperatingSystem = RuntimeInformation.OSDescription; - - telemetry.Context.Component.Version = typeof(WindowsTelemetryInitializer).Assembly.GetName().Version!.ToString(); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Development.json new file mode 100644 index 0000000000..2b7a979354 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Development.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Production.json new file mode 100644 index 0000000000..2b7a979354 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.Production.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://json.schemastore.org/appsettings.json" +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.json new file mode 100644 index 0000000000..610c1fa653 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/appsettings.json @@ -0,0 +1,7 @@ +{ + "WindowsUpdate": { + "FilesUrl": null, + "AutoReload": true + }, + "$schema": "https://json.schemastore.org/appsettings.json" +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props index 0e57992ab7..fb789b23f0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props @@ -1,4 +1,4 @@ -<!-- Generated by bit-bp template v-8.11.0 --> +<!-- Generated by bit-bp template v-8.12.0 --> <Project> <!--/-:msbuild-conditional:noEmit --> <PropertyGroup> @@ -6,6 +6,7 @@ <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <GenerateDocumentationFile>true</GenerateDocumentationFile> + <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <NoWarn>$(NoWarn);CS1998;CS1591</NoWarn> <WarningsAsErrors>$(WarningsAsErrors);CS0114</WarningsAsErrors> @@ -15,11 +16,6 @@ <NeutralLanguage>en-US</NeutralLanguage> <MultilingualEnabled>true</MultilingualEnabled> <PwaEnabled>false</PwaEnabled> - <BlazorWebAssemblyStandalone>false</BlazorWebAssemblyStandalone> - <!-- By enabling the BlazorWebAssemblyStandalone option, you gain the ability to - deploy your Client.Web project on platforms such as GitHub/Cloudflare Pages and Azure Static Web Apps. --> - - <PwaEnabled Condition=" '$(BlazorWebAssemblyStandalone)' == 'true' ">true</PwaEnabled> <!-- See Boilerplate.Shared.AppEnvironment for more info. --> <Environment Condition="'$(Environment)' == '' AND '$(Configuration)' == 'Debug'">Development</Environment> @@ -27,7 +23,7 @@ <SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-ios'))">14.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-mac'))">14.0</SupportedOSPlatformVersion> - <SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-android'))">24.0</SupportedOSPlatformVersion> + <SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-android'))">26.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-windows10'))">10.0.17763.0</SupportedOSPlatformVersion> <TargetPlatformMinVersion Condition="$(TargetFramework.Contains('-windows10'))">10.0.17763.0</TargetPlatformMinVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion> @@ -36,15 +32,20 @@ <DefineConstants Condition=" $(TargetFramework.Contains('-windows')) ">$(DefineConstants);Windows</DefineConstants> <DefineConstants Condition=" $(TargetFramework.Contains('-mac')) ">$(DefineConstants);Mac</DefineConstants> <DefineConstants Condition=" '$(MultilingualEnabled)' == 'true' ">$(DefineConstants);MultilingualEnabled</DefineConstants> - <DefineConstants Condition=" '$(BlazorWebAssemblyStandalone)' == 'true' ">$(DefineConstants);BlazorWebAssemblyStandalone</DefineConstants> <DefineConstants Condition=" '$(PwaEnabled)' == 'true' ">$(DefineConstants);PwaEnabled</DefineConstants> <DefineConstants>$(DefineConstants);$(Environment)</DefineConstants> </PropertyGroup> <!--/+:msbuild-conditional:noEmit --> + <ItemGroup> + <PackageReference Include="Bit.CodeAnalyzers" PrivateAssets="all" /> + <PackageReference Include="Bit.SourceGenerators" PrivateAssets="all" /> + </ItemGroup> + <ItemGroup> <Using Include="System.Globalization" /> <Using Include="System.Net.Http" /> + <Using Include="System.Text.Json" /> <Using Include="System.Resources" /> <Using Include="System.Security.Claims" /> <Using Include="System.Text.Json.Serialization" /> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props new file mode 100644 index 0000000000..3e970cb07a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -0,0 +1,96 @@ +<Project> + <ItemGroup> + <!-- https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management --> + <PackageVersion Include="Bit.Butil" Version="8.12.0" /> + <PackageVersion Include="Bit.BlazorUI" Version="8.12.0" /> + <PackageVersion Include="Bit.BlazorUI.Icons" Version="8.12.0" /> + <PackageVersion Include="Bit.BlazorUI.Assets" Version="8.12.0" /> + <PackageVersion Include="Bit.BlazorUI.Extras" Version="8.12.0" /> + <PackageVersion Include="Bit.Bswup" Version="8.12.0" /> + <PackageVersion Include="Bit.CodeAnalyzers" Version="8.12.0" /> + <PackageVersion Include="Bit.SourceGenerators" Version="8.12.0" /> + <PackageVersion Include="libphonenumber-csharp" Version="8.13.49" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.11" /> + <PackageVersion Include="EmbedIO" Version="3.5.2" /> + <PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" /> + <PackageVersion Include="Microsoft.Maui.Controls" Version="8.0.93" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.93" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="8.0.93" /> + <PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2849.39" /> + <PackageVersion Include="Velopack" Version="0.0.869" /> + <PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="8.0.1" /> + <PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" /> + <PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" /> + <PackageVersion Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.1" /> + <PackageVersion Include="Microsoft.Extensions.Logging.EventSource" Version="8.0.1" /> + <!--/+:msbuild-conditional:noEmit --> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Activity" Version="1.9.3.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Activity.Ktx" Version="1.9.3.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Collection" Version="1.4.5.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Collection.Ktx" Version="1.4.5.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.LiveData.Core.Ktx" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.Runtime" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.Runtime.Ktx" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModel.Ktx" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.AndroidX.Lifecycle.ViewModelSavedState" Version="2.8.7.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Xamarin.Firebase.Messaging" Version="124.0.3.1" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="Plugin.LocalNotification" Version="11.1.4" /> + <PackageVersion Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="AdsPush" Version="2.0.0" /> + <PackageVersion Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Bit.Besql" Version="8.12.0" /> + <PackageVersion Condition=" '$(signalR)' == 'true' OR '$(signalR)' == ''" Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.11" /> + <PackageVersion Condition="'$(sample)' == 'Admin' OR '$(sample)' == ''" Include="Newtonsoft.Json" Version="13.0.3" /> + <PackageVersion Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Analytics" Version="5.0.6" /> + <PackageVersion Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="Microsoft.AppCenter.Crashes" Version="5.0.6" /> + <PackageVersion Condition=" '$(appCenter)' == 'true' OR '$(appCenter)' == '' " Include="West.Extensions.Logging.AppCenter" Version="2.2.2" /> + <PackageVersion Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.22.0" /> + <PackageVersion Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="BlazorApplicationInsights" Version="3.1.0" /> + <PackageVersion Condition=" '$(database)' == 'SqlServer' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" /> + <PackageVersion Condition=" ('$(offlineDb)' == 'true' OR '$(offlineDb)' == '') OR ('$(database)' == 'Sqlite' OR '$(database)' == '') " Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11" /> + <PackageVersion Condition=" '$(database)' == 'PostgreSQL' OR '$(database)' == '' " Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" /> + <PackageVersion Condition=" '$(database)' == 'Cosmos' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.Cosmos" Version="8.0.11" /> + <PackageVersion Condition=" '$(database)' == 'MySql' OR '$(database)' == '' " Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" /> + <PackageVersion Condition=" '$(filesStorage)' == 'AzureBlobStorage' OR '$(filesStorage)' == '' " Include="FluentStorage.Azure.Blobs" Version="5.3.0" /> + <PackageVersion Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" /> + <PackageVersion Condition=" '$(pipeline)' == 'GitHub' OR '$(pipeline)' == '' " Include="GitHubActionsTestLogger" Version="2.4.1" /> + <PackageVersion Condition=" '$(pipeline)' == 'Azure' " Include="AzurePipelines.TestLogger" Version="1.2.3" /> + <PackageVersion Condition=" '$(advancedTests)' == 'true' OR '$(advancedTests)' == '' " Include="MsgReader" Version="5.7.0" /> + <!--/-:msbuild-conditional:noEmit --> + <PackageVersion Include="Humanizer" Version="2.14.1" /> + <PackageVersion Include="QRCoder" Version="1.6.0" /> + <PackageVersion Include="Magick.NET-Q16-AnyCPU" Version="14.1.0" /> + <PackageVersion Include="FluentEmail.Smtp" Version="3.0.2" /> + <PackageVersion Include="FluentStorage" Version="5.6.0" /> + <PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.OData" Version="9.0.0" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.11" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11" /> + <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" /> + <PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Authentication.Twitter" Version="8.0.11" /> + <PackageVersion Include="AspNet.Security.OAuth.GitHub" Version="8.3.0" /> + <PackageVersion Include="Riok.Mapperly" Version="4.1.0" /> + <PackageVersion Include="Twilio" Version="7.5.0" /> + <PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.11" /> + <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.11" /> + <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Localization" Version="8.0.11" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" /> + <PackageVersion Include="System.Text.Json" Version="9.0.0" /> + <PackageVersion Include="coverlet.collector" Version="6.0.2" /> + <PackageVersion Include="FakeItEasy" Version="8.3.0" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> + <PackageVersion Include="Microsoft.Playwright.MSTest" Version="1.48.0" /> + <PackageVersion Include="MSTest.TestAdapter" Version="3.6.3" /> + <PackageVersion Include="MSTest.TestFramework" Version="3.6.3" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/.config/dotnet-tools.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/.config/dotnet-tools.json index 071b4ee500..40d4373bd4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/.config/dotnet-tools.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.8", + "version": "8.0.10", "commands": [ "dotnet-ef" ] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppSettings.cs deleted file mode 100644 index a8d4ec6120..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppSettings.cs +++ /dev/null @@ -1,90 +0,0 @@ -//+:cnd:noEmit -namespace Boilerplate.Server.Api; - -public partial class AppSettings : IValidatableObject -{ - public IdentitySettings Identity { get; set; } = default!; - - public EmailSettings Email { get; set; } = default!; - - public SmsSettings Sms { get; set; } = default!; - - [Required] - public string UserProfileImagesDir { get; set; } = default!; - - //#if (captcha == "reCaptcha") - [Required] - public string GoogleRecaptchaSecretKey { get; set; } = default!; - //#endif - - public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) - { - var validationResults = new List<ValidationResult>(); - - Validator.TryValidateObject(Identity, new ValidationContext(Identity), validationResults, true); - Validator.TryValidateObject(Email, new ValidationContext(Email), validationResults, true); - Validator.TryValidateObject(Sms, new ValidationContext(Sms), validationResults, true); - - return validationResults; - } -} - -public partial class IdentitySettings : IdentityOptions -{ - public TimeSpan BearerTokenExpiration { get; set; } - public TimeSpan RefreshTokenExpiration { get; set; } - - [Required] - public string Issuer { get; set; } = default!; - - [Required] - public string Audience { get; set; } = default!; - - /// <summary> - /// To either confirm and/or change email - /// </summary> - public TimeSpan EmailTokenLifetime { get; set; } - /// <summary> - /// To either confirm and/or change phone number - /// </summary> - public TimeSpan PhoneNumberTokenLifetime { get; set; } - public TimeSpan ResetPasswordTokenLifetime { get; set; } - public TimeSpan TwoFactorTokenLifetime { get; set; } - - /// <summary> - /// To sign in with either Otp or magic link. - /// </summary> - public TimeSpan OtpTokenLifetime { get; set; } - - public TimeSpan RevokeUserSessionsDelay { get; set; } -} - -public partial class EmailSettings -{ - [Required] - public string Host { get; set; } = default!; - /// <summary> - /// If true, the web app tries to store emails as .eml file in the App_Data/sent-emails folder instead of sending them using smtp server (recommended for testing purposes only). - /// </summary> - public bool UseLocalFolderForEmails => Host is "LocalFolder"; - - [Range(1, 65535)] - public int Port { get; set; } - public string? UserName { get; set; } - public string? Password { get; set; } - - [Required] - public string DefaultFromEmail { get; set; } = default!; - public bool HasCredential => (string.IsNullOrEmpty(UserName) is false) && (string.IsNullOrEmpty(Password) is false); -} - -public partial class SmsSettings -{ - public string? FromPhoneNumber { get; set; } - public string? TwilioAccountSid { get; set; } - public string? TwilioAutoToken { get; set; } - - public bool Configured => string.IsNullOrEmpty(FromPhoneNumber) is false && - string.IsNullOrEmpty(TwilioAccountSid) is false && - string.IsNullOrEmpty(TwilioAutoToken) is false; -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj index 596a55a650..f729be6517 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj @@ -6,13 +6,6 @@ <CoreCompileDependsOn>PrepareResources;$(CompileDependsOn)</CoreCompileDependsOn> </PropertyGroup> - <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" /> - <PackageReference Include="Humanizer" Version="2.14.1" /> - </ItemGroup> - <ItemGroup> <Using Include="Microsoft.Extensions.Options" /> <Using Include="Microsoft.AspNetCore.Authorization" /> @@ -22,29 +15,33 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="QRCoder" Version="1.6.0" /> - <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.0.0" /> - <PackageReference Include="FluentEmail.Smtp" Version="3.0.2" /> - <PackageReference Include="FluentStorage" Version="5.5.1" /> - <PackageReference Condition=" '$(filesStorage)' == 'AzureBlobStorage' OR '$(filesStorage)' == '' " Include="FluentStorage.Azure.Blobs" Version="5.2.5" /> - <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.OData" Version="9.0.0" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8" PrivateAssets="all" /> - <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" /> - <PackageReference Condition=" '$(database)' == 'SqlServer' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" /> - <PackageReference Condition=" '$(database)' == 'Sqlite' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> - <PackageReference Condition=" '$(database)' == 'PostgreSQL' OR '$(database)' == '' " Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" /> - <PackageReference Condition=" '$(database)' == 'Cosmos' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.Cosmos" Version="8.0.8" /> - <PackageReference Condition=" '$(database)' == 'MySql' OR '$(database)' == '' " Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8" PrivateAssets="all" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" /> - <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="8.0.8" /> - <PackageReference Include="AspNet.Security.OAuth.GitHub" Version="8.1.0" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" /> - <PackageReference Include="Twilio" Version="7.3.0" /> + <PackageReference Include="libphonenumber-csharp" /> + <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.ApplicationInsights.AspNetCore" /> + <PackageReference Include="Humanizer" /> + <PackageReference Include="QRCoder" /> + <PackageReference Include="Magick.NET-Q16-AnyCPU" /> + <PackageReference Include="FluentEmail.Smtp" /> + <PackageReference Include="FluentStorage" /> + <PackageReference Condition=" '$(filesStorage)' == 'AzureBlobStorage' OR '$(filesStorage)' == '' " Include="FluentStorage.Azure.Blobs" /> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> + <PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" /> + <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" /> + <PackageReference Include="Microsoft.AspNetCore.OData" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" /> + <PackageReference Include="Microsoft.EntityFrameworkCore" /> + <PackageReference Condition=" '$(database)' == 'SqlServer' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.SqlServer" /> + <PackageReference Condition=" '$(database)' == 'Sqlite' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.Sqlite" /> + <PackageReference Condition=" '$(database)' == 'PostgreSQL' OR '$(database)' == '' " Include="Npgsql.EntityFrameworkCore.PostgreSQL" /> + <PackageReference Condition=" '$(database)' == 'Cosmos' OR '$(database)' == '' " Include="Microsoft.EntityFrameworkCore.Cosmos" /> + <PackageReference Condition=" '$(database)' == 'MySql' OR '$(database)' == '' " Include="Pomelo.EntityFrameworkCore.MySql" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" PrivateAssets="all" /> + <PackageReference Include="Swashbuckle.AspNetCore" /> + <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" /> + <PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" /> + <PackageReference Include="AspNet.Security.OAuth.GitHub" /> + <PackageReference Include="Riok.Mapperly" /> + <PackageReference Include="Twilio" /> + <PackageReference Condition=" '$(notification)' == 'true' OR '$(notification)' == ''" Include="AdsPush" /> <Using Include="Microsoft.EntityFrameworkCore.Migrations" /> <Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" /> @@ -93,7 +90,7 @@ --> <PropertyGroup> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> - <EnableSdkContainerDebugging Condition="'$(Configuration)' == 'Debug'">True</EnableSdkContainerDebugging> + <EnableSdkContainerDebugging Condition="'$(Environment)' == 'Production'">True</EnableSdkContainerDebugging> </PropertyGroup> </Project> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor index 2259a756fd..d6ff895fc6 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Components/SocialSignedInPage.razor @@ -7,6 +7,7 @@ <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="Content-TypeEntity" content="text/html; charset=UTF-8" /> + <link rel="icon" href="data:;base64,iVBORw0KGgo="> <title>@Localizer[nameof(AppStrings.SocialSignedInTitle)]</title> <style> html, body, main { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs index 097a197826..e42b3631f0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs @@ -2,7 +2,7 @@ public partial class AppControllerBase : ControllerBase { - [AutoInject] protected AppSettings AppSettings = default!; + [AutoInject] protected ServerApiSettings AppSettings = default!; [AutoInject] protected IConfiguration Configuration = default!; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs index fcf9553b7d..f5ec34cbf0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.StaticFiles; -using Boilerplate.Server.Api.Models.Identity; +using Boilerplate.Server.Api.Models.Identity; using FluentStorage.Blobs; using ImageMagick; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs index e70fbaf373..e2f39ea182 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs @@ -1,4 +1,9 @@ -using Boilerplate.Shared.Dtos.Categories; +//+:cnd:noEmit +using Boilerplate.Server.Api.SignalR; +//#if (signalR == true) +using Microsoft.AspNetCore.SignalR; +//#endif +using Boilerplate.Shared.Dtos.Categories; using Boilerplate.Shared.Controllers.Categories; namespace Boilerplate.Server.Api.Controllers; @@ -6,6 +11,10 @@ namespace Boilerplate.Server.Api.Controllers; [ApiController, Route("api/[controller]/[action]")] public partial class CategoryController : AppControllerBase, ICategoryController { + //#if (signalR == true) + [AutoInject] private IHubContext<AppHub> appHubContext = default!; + //#endif + [HttpGet, EnableQuery] public IQueryable<CategoryDto> Get() { @@ -20,11 +29,8 @@ public async Task<PagedResult<CategoryDto>> GetCategories(ODataQueryOptions<Cate var totalCount = await query.LongCountAsync(cancellationToken); - if (odataQuery.Skip is not null) - query = query.Skip(odataQuery.Skip.Value); - - if (odataQuery.Top is not null) - query = query.Take(odataQuery.Top.Value); + query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip!.Value) + .TakeIf(odataQuery.Top is not null, odataQuery.Top!.Value); return new PagedResult<CategoryDto>(await query.ToArrayAsync(cancellationToken), totalCount); } @@ -49,38 +55,51 @@ public async Task<CategoryDto> Create(CategoryDto dto, CancellationToken cancell await DbContext.SaveChangesAsync(cancellationToken); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + return entityToAdd.Map(); } [HttpPut] public async Task<CategoryDto> Update(CategoryDto dto, CancellationToken cancellationToken) { - var entityToUpdate = await DbContext.Categories.FirstOrDefaultAsync(t => t.Id == dto.Id, cancellationToken); + var entityToUpdate = dto.Map(); - if (entityToUpdate is null) - throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ProductCouldNotBeFound)]); - - dto.Patch(entityToUpdate); + DbContext.Update(entityToUpdate); await DbContext.SaveChangesAsync(cancellationToken); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + return entityToUpdate.Map(); } - [HttpDelete("{id}")] - public async Task Delete(Guid id, CancellationToken cancellationToken) + [HttpDelete("{id}/{concurrencyStamp}")] + public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) { if (await DbContext.Products.AnyAsync(p => p.CategoryId == id, cancellationToken)) { throw new BadRequestException(Localizer[nameof(AppStrings.CategoryNotEmpty)]); } - DbContext.Categories.Remove(new() { Id = id }); + DbContext.Categories.Remove(new() { Id = id, ConcurrencyStamp = Convert.FromBase64String(Uri.UnescapeDataString(concurrencyStamp)) }); - var affectedRows = await DbContext.SaveChangesAsync(cancellationToken); + await DbContext.SaveChangesAsync(cancellationToken); - if (affectedRows < 1) - throw new ResourceNotFoundException(Localizer[nameof(AppStrings.CategoryCouldNotBeFound)]); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + } + + //#if (signalR == true) + private async Task PublishDashboardDataChanged(CancellationToken cancellationToken) + { + await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, cancellationToken); } + //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Dashboard/DashboardController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Dashboard/DashboardController.cs index ee4a28f8df..7a2ac7701f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Dashboard/DashboardController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Dashboard/DashboardController.cs @@ -15,34 +15,23 @@ public async Task<OverallAnalyticsStatsDataResponseDto> GetOverallAnalyticsStats result.TotalProducts = await DbContext.Products.CountAsync(cancellationToken); result.Last30DaysProductCount = await DbContext.Products.CountAsync(p => p.CreatedOn > last30DaysDate, cancellationToken); + result.TotalCategories = await DbContext.Categories.CountAsync(cancellationToken); + result.CategoriesWithProductCount = await DbContext.Categories.CountAsync(c => c.Products.Count > 0, cancellationToken); return result; } [HttpGet] - public IQueryable<ProductsCountPerCategoryResponseDto> GetProductsCountPerCategoryStats() + public async Task<IQueryable<ProductsCountPerCategoryResponseDto>> GetProductsCountPerCategoryStats() { return DbContext.Categories - .Select(c => new ProductsCountPerCategoryResponseDto() - { - CategoryName = c.Name, - CategoryColor = c.Color, - ProductCount = c.Products!.Count() - }); - } - - [HttpGet] - public IQueryable<ProductSaleStatResponseDto> GetProductsSalesStats() - { - Random rand = new Random(); - return DbContext.Products.Include(p => p.Category) - .Select(p => new ProductSaleStatResponseDto() - { - ProductName = p.Name, - CategoryColor = p.Category!.Color, - SaleAmount = rand.Next(1, 10) * p.Price - }); + .Select(c => new ProductsCountPerCategoryResponseDto() + { + CategoryName = c.Name, + CategoryColor = c.Color, + ProductCount = c.Products!.Count() + }); } @@ -51,17 +40,14 @@ public async Task<List<ProductPercentagePerCategoryResponseDto>> GetProductsPerc { var productsTotalCount = await DbContext.Products.CountAsync(cancellationToken); - if (productsTotalCount == 0) - { - return []; - } + if (productsTotalCount == 0) return []; return await DbContext.Categories - .Select(c => new ProductPercentagePerCategoryResponseDto() - { - CategoryName = c!.Name, - CategoryColor = c.Color, - ProductPercentage = (float)decimal.Divide(c.Products!.Count(), productsTotalCount) * 100 - }).ToListAsync(cancellationToken); + .Select(c => new ProductPercentagePerCategoryResponseDto() + { + CategoryName = c!.Name, + CategoryColor = c.Color, + ProductPercentage = (float)decimal.Divide(c.Products!.Count(), productsTotalCount) * 100 + }).ToListAsync(cancellationToken); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.EmailConfirmation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.EmailConfirmation.cs index 924e6535a5..94eb8ed520 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.EmailConfirmation.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.EmailConfirmation.cs @@ -44,7 +44,6 @@ public async Task ConfirmEmail(ConfirmEmailRequestDto request, CancellationToken throw new BadRequestException(nameof(AppStrings.InvalidToken)); } - var userEmailStore = (IUserEmailStore<User>)userStore; await userEmailStore.SetEmailConfirmedAsync(user, true, cancellationToken); var result = await userManager.UpdateAsync(user); if (result.Succeeded is false) @@ -56,7 +55,7 @@ public async Task ConfirmEmail(ConfirmEmailRequestDto request, CancellationToken if (updateResult.Succeeded is false) throw new ResourceValidationException(updateResult.Errors.Select(e => new LocalizedString(e.Code, e.Description)).ToArray()); - var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp,{user.OtpRequestedOn?.ToUniversalTime()}")); + var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_Email,{user.OtpRequestedOn?.ToUniversalTime()}")); await SignIn(new() { Email = request.Email, Otp = token }, cancellationToken); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs index fc58f332bb..eb8bdcff8a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs @@ -8,11 +8,12 @@ namespace Boilerplate.Server.Api.Controllers.Identity; public partial class IdentityController { - [AutoInject] private SmsService smsService = default!; + [AutoInject] private PhoneService phoneService = default!; [HttpPost] public async Task SendConfirmPhoneToken(SendPhoneTokenRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindByPhoneNumber(request.PhoneNumber!) ?? throw new BadRequestException(Localizer[nameof(AppStrings.UserNotFound)]); @@ -25,6 +26,7 @@ public async Task SendConfirmPhoneToken(SendPhoneTokenRequestDto request, Cancel [HttpPost, Produces<TokenResponseDto>()] public async Task ConfirmPhone(ConfirmPhoneRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindByPhoneNumber(request.PhoneNumber!) ?? throw new BadRequestException(Localizer[nameof(AppStrings.UserNotFound)]); @@ -54,7 +56,7 @@ public async Task ConfirmPhone(ConfirmPhoneRequestDto request, CancellationToken if (updateResult.Succeeded is false) throw new ResourceValidationException(updateResult.Errors.Select(e => new LocalizedString(e.Code, e.Description)).ToArray()); - var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp,{user.OtpRequestedOn?.ToUniversalTime()}")); + var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_Sms,{user.OtpRequestedOn?.ToUniversalTime()}")); await SignIn(new() { PhoneNumber = request.PhoneNumber, Otp = token }, cancellationToken); } @@ -77,6 +79,6 @@ private async Task SendConfirmPhoneToken(User user, CancellationToken cancellati var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"VerifyPhoneNumber:{phoneNumber},{user.PhoneNumberTokenRequestedOn?.ToUniversalTime()}")); var link = new Uri(HttpContext.Request.GetWebClientUrl(), $"{Urls.ConfirmPage}?phoneNumber={Uri.EscapeDataString(phoneNumber!)}&phoneToken={Uri.EscapeDataString(token)}&culture={CultureInfo.CurrentUICulture.Name}"); - await smsService.SendSms(Localizer[nameof(AppStrings.ConfirmPhoneTokenSmsText), token], phoneNumber, cancellationToken); + await phoneService.SendSms(Localizer[nameof(AppStrings.ConfirmPhoneTokenSmsText), token], phoneNumber, cancellationToken); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs index f1f0e6b373..3544df9e41 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs @@ -2,6 +2,9 @@ using Humanizer; using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Server.Api.Models.Identity; +//#if (signalR == true) +using Microsoft.AspNetCore.SignalR; +//#endif namespace Boilerplate.Server.Api.Controllers.Identity; @@ -10,6 +13,7 @@ public partial class IdentityController [HttpPost] public async Task SendResetPasswordToken(SendResetPasswordTokenRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]); @@ -34,28 +38,35 @@ public async Task SendResetPasswordToken(SendResetPasswordTokenRequestDto reques var url = $"{Urls.ResetPasswordPage}?token={Uri.EscapeDataString(token)}&{qs}&culture={CultureInfo.CurrentUICulture.Name}"; var link = new Uri(HttpContext.Request.GetWebClientUrl(), url); - async Task SendEmail() - { - if (await userManager.IsEmailConfirmedAsync(user) is false) return; + List<Task> sendMessagesTasks = []; - await emailService.SendResetPasswordToken(user, token, link, cancellationToken); + if (await userManager.IsEmailConfirmedAsync(user)) + { + sendMessagesTasks.Add(emailService.SendResetPasswordToken(user, token, link, cancellationToken)); } - async Task SendSms() - { - if (await userManager.IsPhoneNumberConfirmedAsync(user) is false) return; + var message = Localizer[nameof(AppStrings.ResetPasswordTokenShortText), token].ToString(); - await smsService.SendSms(Localizer[nameof(AppStrings.ResetPasswordTokenSmsText), token], user.PhoneNumber!, cancellationToken); + if (await userManager.IsPhoneNumberConfirmedAsync(user)) + { + sendMessagesTasks.Add(phoneService.SendSms(message, user.PhoneNumber!, cancellationToken)); } - await Task.WhenAll([SendEmail(), SendSms()]); - } + //#if (signalR == true) + sendMessagesTasks.Add(appHubContext.Clients.User(user.Id.ToString()).SendAsync(SignalREvents.SHOW_MESSAGE, message, cancellationToken)); + //#endif + //#if (notification == true) + sendMessagesTasks.Add(pushNotificationService.RequestPush(message: message, customDeviceFilter: d => d.UserId == user.Id, cancellationToken: cancellationToken)); + //#endif + await Task.WhenAll(sendMessagesTasks); + } [HttpPost] public async Task ResetPassword(ResetPasswordRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]); var expired = (DateTimeOffset.Now - user.ResetPasswordTokenRequestedOn) > AppSettings.Identity.ResetPasswordTokenLifetime; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs index bafcdf8806..956c4b48bf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs @@ -1,7 +1,6 @@ //+:cnd:noEmit using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Components.Web; -using Boilerplate.Server.Api.Models.Identity; namespace Boilerplate.Server.Api.Controllers.Identity; @@ -34,7 +33,7 @@ public async Task<ActionResult> SocialSignInCallback(string? returnUrl = null, i try { var email = info.Principal.GetEmail(); - var phoneNumber = info.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.HomePhone or ClaimTypes.MobilePhone or ClaimTypes.OtherPhone)?.Value; + var phoneNumber = phoneService.NormalizePhoneNumber(info.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.HomePhone or ClaimTypes.MobilePhone or ClaimTypes.OtherPhone)?.Value); var user = await userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey); @@ -53,12 +52,12 @@ public async Task<ActionResult> SocialSignInCallback(string? returnUrl = null, i if (string.IsNullOrEmpty(email) is false) { - await ((IUserEmailStore<User>)userStore).SetEmailAsync(user, email, cancellationToken); + await userEmailStore.SetEmailAsync(user, email, cancellationToken); } if (string.IsNullOrEmpty(phoneNumber) is false) { - await ((IUserPhoneNumberStore<User>)userStore).SetPhoneNumberAsync(user, phoneNumber!, cancellationToken); + await userPhoneNumberStore.SetPhoneNumberAsync(user, phoneNumber!, cancellationToken); } var result = await userManager.CreateAsync(user, password: Guid.NewGuid().ToString("N") /* Users can reset their password later. */); @@ -73,17 +72,17 @@ public async Task<ActionResult> SocialSignInCallback(string? returnUrl = null, i if (string.IsNullOrEmpty(email) is false && email == user.Email && await userManager.IsEmailConfirmedAsync(user) is false) { - await ((IUserEmailStore<User>)userStore).SetEmailConfirmedAsync(user, true, cancellationToken); + await userEmailStore.SetEmailConfirmedAsync(user, true, cancellationToken); await userManager.UpdateAsync(user); } if (string.IsNullOrEmpty(phoneNumber) is false && phoneNumber == user.PhoneNumber && await userManager.IsPhoneNumberConfirmedAsync(user) is false) { - await ((IUserPhoneNumberStore<User>)userStore).SetPhoneNumberConfirmedAsync(user, true, cancellationToken); + await userPhoneNumberStore.SetPhoneNumberConfirmedAsync(user, true, cancellationToken); await userManager.UpdateAsync(user); } - (_, url) = await GenerateOtpTokenData(user, returnUrl); // Sign in with a magic link, and 2FA will be prompted if already enabled. + (_, url) = await GenerateAutomaticSignInLink(user, returnUrl, originalAuthenticationMethod: "Social"); // Sign in with a magic link, and 2FA will be prompted if already enabled. } catch (Exception exp) { @@ -96,8 +95,11 @@ public async Task<ActionResult> SocialSignInCallback(string? returnUrl = null, i } if (localHttpPort is not null) return Redirect(new Uri(new Uri($"http://localhost:{localHttpPort}"), url).ToString()); - var webClientUrl = Configuration.GetValue<string?>("WebClientUrl"); + var webClientUrl = Configuration.Get<ServerApiSettings>()!.WebClientUrl; if (string.IsNullOrEmpty(webClientUrl) is false) return Redirect(new Uri(new Uri(webClientUrl), url).ToString()); return LocalRedirect($"~{url}"); } + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to perform {loginProvider} social sign in for {principal}")] + private static partial void LogSocialSignInCallbackFailed(ILogger logger, Exception exp, string loginProvider, string principal); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs index d406468ad0..88a97b1f5f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs @@ -1,14 +1,15 @@ //+:cnd:noEmit using Humanizer; using Microsoft.AspNetCore.Authentication.BearerToken; -//#if (signalr == true) +//#if (signalR == true) using Microsoft.AspNetCore.SignalR; -using Boilerplate.Server.Api.Hubs; +using Boilerplate.Server.Api.SignalR; //#endif using Boilerplate.Server.Api.Services; using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Server.Api.Models.Identity; using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Server.Api.Services.Identity; namespace Boilerplate.Server.Api.Controllers.Identity; @@ -17,15 +18,20 @@ namespace Boilerplate.Server.Api.Controllers.Identity; public partial class IdentityController : AppControllerBase, IIdentityController { [AutoInject] private IUserStore<User> userStore = default!; + [AutoInject] private IUserEmailStore<User> userEmailStore = default!; + [AutoInject] private IUserPhoneNumberStore<User> userPhoneNumberStore = default!; [AutoInject] private UserManager<User> userManager = default!; [AutoInject] private SignInManager<User> signInManager = default!; [AutoInject] private ILogger<IdentityController> logger = default!; [AutoInject] private IUserConfirmation<User> userConfirmation = default!; [AutoInject] private IOptionsMonitor<BearerTokenOptions> bearerTokenOptions = default!; - [AutoInject] private IUserClaimsPrincipalFactory<User> userClaimsPrincipalFactory = default!; - //#if (signalr == true) + [AutoInject] private AppUserClaimsPrincipalFactory userClaimsPrincipalFactory = default!; + //#if (signalR == true) [AutoInject] private IHubContext<AppHub> appHubContext = default!; //#endif + //#if (notification == true) + [AutoInject] private PushNotificationService pushNotificationService = default!; + //#endif //#if (captcha == "reCaptcha") [AutoInject] private GoogleRecaptchaHttpClient googleRecaptchaHttpClient = default!; @@ -38,6 +44,7 @@ public partial class IdentityController : AppControllerBase, IIdentityController [HttpPost] public async Task SignUp(SignUpRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); //#if (captcha == "reCaptcha") if (await googleRecaptchaHttpClient.Verify(request.GoogleRecaptchaResponse, cancellationToken) is false) throw new BadRequestException(Localizer[nameof(AppStrings.InvalidGoogleRecaptchaResponse)]); @@ -54,12 +61,12 @@ public async Task SignUp(SignUpRequestDto request, CancellationToken cancellatio if (string.IsNullOrEmpty(request.Email) is false) { - await ((IUserEmailStore<User>)userStore).SetEmailAsync(userToAdd, request.Email!, cancellationToken); + await userEmailStore.SetEmailAsync(userToAdd, request.Email!, cancellationToken); } if (string.IsNullOrEmpty(request.PhoneNumber) is false) { - await ((IUserPhoneNumberStore<User>)userStore).SetPhoneNumberAsync(userToAdd, request.PhoneNumber!, cancellationToken); + await userPhoneNumberStore.SetPhoneNumberAsync(userToAdd, request.PhoneNumber!, cancellationToken); } var result = await userManager.CreateAsync(userToAdd, request.Password!); @@ -83,6 +90,7 @@ public async Task SignUp(SignUpRequestDto request, CancellationToken cancellatio [HttpPost, Produces<SignInResponseDto>()] public async Task SignIn(SignInRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); signInManager.AuthenticationScheme = IdentityConstants.BearerScheme; var user = await userManager.FindUserAsync(request) ?? throw new UnauthorizedException(Localizer[nameof(AppStrings.InvalidUserCredentials)]); @@ -91,37 +99,21 @@ public async Task SignIn(SignInRequestDto request, CancellationToken cancellatio bool isOtpSignIn = string.IsNullOrEmpty(request.Otp) is false; - if (isOtpSignIn) - { - var expired = (DateTimeOffset.Now - user.OtpRequestedOn) > AppSettings.Identity.OtpTokenLifetime; - - if (expired) - throw new BadRequestException(nameof(AppStrings.ExpiredToken)); - } - - var result = isOtpSignIn + var (signInResult, firstStepAuthenticationMethod) = isOtpSignIn ? await signInManager.OtpSignInAsync(user, request.Otp!) - : await signInManager.PasswordSignInAsync(user!.UserName!, request.Password!, isPersistent: false, lockoutOnFailure: true); + : (await signInManager.PasswordSignInAsync(user!.UserName!, request.Password!, isPersistent: false, lockoutOnFailure: true), authenticationMethod: "Password"); - if (result.IsNotAllowed && await userConfirmation.IsConfirmedAsync(userManager, user) is false) + if (signInResult.IsNotAllowed && await userConfirmation.IsConfirmedAsync(userManager, user) is false) throw new BadRequestException(Localizer[nameof(AppStrings.UserIsNotConfirmed)]); - if (result.IsLockedOut) + if (signInResult.IsLockedOut) throw new BadRequestException(Localizer[nameof(AppStrings.UserLockedOut), (DateTimeOffset.UtcNow - user.LockoutEnd!).Value.Humanize(culture: CultureInfo.CurrentUICulture)]); - if (result.RequiresTwoFactor) + if (signInResult.RequiresTwoFactor) { - if (string.IsNullOrEmpty(request.TwoFactorRecoveryCode) is false) - { - result = await signInManager.TwoFactorRecoveryCodeSignInAsync(request.TwoFactorRecoveryCode); - } - else if (string.IsNullOrEmpty(request.TwoFactorToken) is false) - { - result = await signInManager.TwoFactorSignInAsync(TokenOptions.DefaultPhoneProvider, request.TwoFactorToken, false, false); - } - else if (string.IsNullOrEmpty(request.TwoFactorCode) is false) + if (string.IsNullOrEmpty(request.TwoFactorCode) is false) { - result = await signInManager.TwoFactorAuthenticatorSignInAsync(request.TwoFactorCode, false, false); + signInResult = await CheckTwoFactorCode(request.TwoFactorCode); } else { @@ -130,7 +122,7 @@ public async Task SignIn(SignInRequestDto request, CancellationToken cancellatio } } - if (result.Succeeded is false) + if (signInResult.Succeeded is false) throw new UnauthorizedException(Localizer[nameof(AppStrings.InvalidUserCredentials)]); if (string.IsNullOrEmpty(request.Otp) is false) @@ -143,11 +135,29 @@ public async Task SignIn(SignInRequestDto request, CancellationToken cancellatio } user.Sessions.Add(userSession); + user.TwoFactorTokenRequestedOn = null; var addUserSessionResult = await userManager.UpdateAsync(user); if (addUserSessionResult.Succeeded is false) throw new ResourceValidationException(addUserSessionResult.Errors.Select(e => new LocalizedString(e.Code, e.Description)).ToArray()); } + private async Task<Microsoft.AspNetCore.Identity.SignInResult> CheckTwoFactorCode(string code) + { + var result = await signInManager.TwoFactorRecoveryCodeSignInAsync(code); + + if (result.Succeeded is false) + { + result = await signInManager.TwoFactorSignInAsync(TokenOptions.DefaultPhoneProvider, code, false, false); + } + + if (result.Succeeded is false) + { + result = await signInManager.TwoFactorAuthenticatorSignInAsync(code, false, false); + } + + return result; + } + /// <summary> /// Creates a user session and adds its ID to the access and refresh tokens, but only if the sign-in is successful. /// </summary> @@ -159,12 +169,12 @@ private UserSession CreateUserSession(string? device) // Relying on Cloudflare cdn to retrieve address. // https://developers.cloudflare.com/rules/transform/managed-transforms/reference/#add-visitor-location-headers Address = $"{Request.Headers["cf-ipcountry"]}, {Request.Headers["cf-ipcity"]}", - Device = device ?? Localizer[nameof(AppStrings.UnknwonDevice)], + Device = device, IP = HttpContext.Connection.RemoteIpAddress?.ToString(), StartedOn = DateTimeOffset.UtcNow }; - ((AppUserClaimsPrincipalFactory)userClaimsPrincipalFactory).SessionClaims.Add(new("session-id", userSession.SessionUniqueId.ToString())); + userClaimsPrincipalFactory.SessionClaims.Add(new("session-id", userSession.SessionUniqueId.ToString())); return userSession; } @@ -172,58 +182,51 @@ private UserSession CreateUserSession(string? device) [HttpPost] public async Task<ActionResult<TokenResponseDto>> Refresh(RefreshRequestDto request) { - var refreshTokenProtector = bearerTokenOptions.Get(IdentityConstants.BearerScheme).RefreshTokenProtector; - var refreshTicket = refreshTokenProtector.Unprotect(request.RefreshToken); + UserSession? userSession = null; + User? user = null; - if (refreshTicket?.Principal.IsAuthenticated() is false) - throw new UnauthorizedException(); + try + { + var refreshTokenProtector = bearerTokenOptions.Get(IdentityConstants.BearerScheme).RefreshTokenProtector; + var refreshTicket = refreshTokenProtector.Unprotect(request.RefreshToken); - string? userId = null; User? user = null; UserSession? userSession = null; + if (refreshTicket?.Principal.IsAuthenticated() is false + || (refreshTicket!.Properties.ExpiresUtc ?? DateTimeOffset.MinValue) < DateTimeOffset.UtcNow) + throw new UnauthorizedException(); - userId = refreshTicket?.Principal?.GetUserId().ToString(); - if (string.IsNullOrEmpty(userId) is false) - { - user = await userManager.FindByIdAsync(userId) ?? throw new UnauthorizedException(); + var userId = refreshTicket!.Principal.GetUserId().ToString() ?? throw new InvalidOperationException("User id could not be found"); + var currentSessionId = Guid.Parse(refreshTicket.Principal.FindFirstValue("session-id") ?? throw new InvalidOperationException("session id could not be found")); + + user = await userManager.FindByIdAsync(userId) ?? throw new UnauthorizedException(); // User might have been deleted. + userSession = user!.Sessions.Find(s => s.SessionUniqueId == currentSessionId) ?? throw new UnauthorizedException(); // User session might have been deleted. + + if (await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not User _) + throw new UnauthorizedException(); + + userSession.RenewedOn = DateTimeOffset.UtcNow; + + userClaimsPrincipalFactory.SessionClaims.Add(new("session-id", currentSessionId.ToString())); + + var newPrincipal = await signInManager.CreateUserPrincipalAsync(user!); + + return SignIn(newPrincipal, authenticationScheme: IdentityConstants.BearerScheme); } - if (Guid.TryParse(refreshTicket?.Principal?.FindFirstValue("session-id"), out var currentSessionId)) + catch when (userSession is not null) { - userSession = user!.Sessions.Find(s => s.SessionUniqueId == currentSessionId); + user!.Sessions.Remove(userSession); + throw; } - - bool isExpiredSession = refreshTicket?.Properties?.ExpiresUtc is not { } expiresUtc || - DateTimeOffset.UtcNow >= expiresUtc || - await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not User _ || - userSession is null; - - if (userSession is not null) + finally { - if (isExpiredSession) - { - user!.Sessions.Remove(userSession); - } - else - { - userSession!.RenewedOn = DateTimeOffset.UtcNow; - } - try { - await userManager.UpdateAsync(user!); + if (user is not null) + { + await userManager.UpdateAsync(user); + } } catch (ConflictException) { /* When access_token gets expired and user navigates to the page that sends multiple requests in parallel, multiple concurrent refresh token api call happens and this will results into concurrency exception during updating session's renewed on. */ } } - - if (isExpiredSession) - { - // Return 401 if refresh token is either invalid or expired. - throw new UnauthorizedException(); - } - - ((AppUserClaimsPrincipalFactory)userClaimsPrincipalFactory).SessionClaims.Add(new("session-id", currentSessionId.ToString())); - - var newPrincipal = await signInManager.CreateUserPrincipalAsync(user!); - - return SignIn(newPrincipal, authenticationScheme: IdentityConstants.BearerScheme); } /// <summary> @@ -232,6 +235,7 @@ await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not U [HttpPost] public async Task SendOtp(IdentityRequestDto request, string? returnUrl = null, CancellationToken cancellationToken = default) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]); @@ -243,32 +247,56 @@ public async Task SendOtp(IdentityRequestDto request, string? returnUrl = null, if (resendDelay < TimeSpan.Zero) throw new TooManyRequestsExceptions(Localizer[nameof(AppStrings.WaitForOtpRequestResendDelay), resendDelay.Value.Humanize(culture: CultureInfo.CurrentUICulture)]); - var (token, url) = await GenerateOtpTokenData(user, returnUrl); + var (magicLinkToken, url) = await GenerateAutomaticSignInLink(user, returnUrl, originalAuthenticationMethod: "Email"); var link = new Uri(HttpContext.Request.GetWebClientUrl(), url); - async Task SendEmail() - { - if (await userManager.IsEmailConfirmedAsync(user) is false) return; + List<Task> sendMessagesTasks = []; - await emailService.SendOtp(user, token, link, cancellationToken); + if (await userManager.IsEmailConfirmedAsync(user)) + { + sendMessagesTasks.Add(emailService.SendOtp(user, magicLinkToken, link, cancellationToken)); } - async Task SendSms() + if (await userManager.IsPhoneNumberConfirmedAsync(user)) { - if (await userManager.IsPhoneNumberConfirmedAsync(user) is false) return; - - await smsService.SendSms(Localizer[nameof(AppStrings.OtpSmsText), token], user.PhoneNumber!, cancellationToken); + var smsMessage = Localizer[nameof(AppStrings.OtpShortText), await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_Sms,{user.OtpRequestedOn?.ToUniversalTime()}"))].ToString(); + sendMessagesTasks.Add(phoneService.SendSms(smsMessage, user.PhoneNumber!, cancellationToken)); } - await Task.WhenAll([SendEmail(), SendSms()]); + //#if (signalR == true || notification == true) + var pushMessage = Localizer[nameof(AppStrings.OtpShortText), await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_Push,{user.OtpRequestedOn?.ToUniversalTime()}"))].ToString(); + //#endif + + //#if (signalR == true) + sendMessagesTasks.Add(appHubContext.Clients.User(user.Id.ToString()).SendAsync(SignalREvents.SHOW_MESSAGE, pushMessage, cancellationToken)); + //#endif + + //#if (notification == true) + sendMessagesTasks.Add(pushNotificationService.RequestPush(message: pushMessage, customDeviceFilter: d => d.UserId == user.Id, cancellationToken: cancellationToken)); + //#endif + + await Task.WhenAll(sendMessagesTasks); } [HttpPost] - public async Task SendTwoFactorToken(IdentityRequestDto request, CancellationToken cancellationToken) + public async Task SendTwoFactorToken(SignInRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]); + if (user.TwoFactorEnabled is false) + throw new BadRequestException(); + + bool isOtpSignIn = string.IsNullOrEmpty(request.Otp) is false; + + var (signInResult, firstStepAuthenticationMethod) = isOtpSignIn + ? await signInManager.OtpSignInAsync(user, request.Otp!) + : (await signInManager.PasswordSignInAsync(user!.UserName!, request.Password!, isPersistent: false, lockoutOnFailure: true), authenticationMethod: "Password"); + + if (signInResult.RequiresTwoFactor is false) + throw new BadRequestException(); + var resendDelay = (DateTimeOffset.Now - user.TwoFactorTokenRequestedOn) - AppSettings.Identity.TwoFactorTokenLifetime; if (resendDelay < TimeSpan.Zero) @@ -281,27 +309,35 @@ public async Task SendTwoFactorToken(IdentityRequestDto request, CancellationTok var token = await userManager.GenerateTwoFactorTokenAsync(user, TokenOptions.DefaultPhoneProvider); - async Task SendEmail() + List<Task> sendMessagesTasks = []; + + if (firstStepAuthenticationMethod != "Email" && await userManager.IsEmailConfirmedAsync(user)) { - if (await userManager.IsEmailConfirmedAsync(user)) - { - await emailService.SendTwoFactorToken(user, token, cancellationToken); - } + sendMessagesTasks.Add(emailService.SendTwoFactorToken(user, token, cancellationToken)); + } + + var message = Localizer[nameof(AppStrings.TwoFactorTokenShortText), token].ToString(); + + if (firstStepAuthenticationMethod != "Sms" && await userManager.IsPhoneNumberConfirmedAsync(user)) + { + sendMessagesTasks.Add(phoneService.SendSms(message, user.PhoneNumber!, cancellationToken)); } - async Task SendSms() + //#if (signalR == true) + if (firstStepAuthenticationMethod != "SignalR") { - if (await userManager.IsPhoneNumberConfirmedAsync(user)) - { - await smsService.SendSms(Localizer[nameof(AppStrings.TwoFactorTokenSmsText), token], user.PhoneNumber!, cancellationToken); - } + sendMessagesTasks.Add(appHubContext.Clients.User(user.Id.ToString()).SendAsync(SignalREvents.SHOW_MESSAGE, message, cancellationToken)); } + //#endif - //#if (signalr == true) - await appHubContext.Clients.User(user.Id.ToString()).SendAsync("TwoFactorToken", token, cancellationToken); + //#if (notification == true) + if (firstStepAuthenticationMethod != "Push") + { + sendMessagesTasks.Add(pushNotificationService.RequestPush(message: message, customDeviceFilter: d => d.UserId == user.Id, cancellationToken: cancellationToken)); + } //#endif - await Task.WhenAll([SendEmail(), SendSms()]); + await Task.WhenAll(sendMessagesTasks); } [HttpGet] @@ -313,10 +349,7 @@ public async Task<ActionResult> SocialSignedIn() return Content(html, "text/html"); } - [LoggerMessage(Level = LogLevel.Error, Message = "Failed to perform {loginProvider} social sign in for {principal}")] - private static partial void LogSocialSignInCallbackFailed(ILogger logger, Exception exp, string loginProvider, string principal); - - private async Task<(string token, string url)> GenerateOtpTokenData(User user, string? returnUrl) + private async Task<(string token, string url)> GenerateAutomaticSignInLink(User user, string? returnUrl, string originalAuthenticationMethod) { user.OtpRequestedOn = DateTimeOffset.Now; @@ -325,7 +358,7 @@ public async Task<ActionResult> SocialSignedIn() if (result.Succeeded is false) throw new ResourceValidationException(result.Errors.Select(e => new LocalizedString(e.Code, e.Description)).ToArray()); - var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp,{user.OtpRequestedOn?.ToUniversalTime()}")); + var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_{originalAuthenticationMethod},{user.OtpRequestedOn?.ToUniversalTime()}")); var qs = $"userName={Uri.EscapeDataString(user.UserName!)}"; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs index f420eb5213..497437d052 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs @@ -1,7 +1,7 @@ using System.Text; using System.Text.Encodings.Web; -using Humanizer; using QRCoder; +using Humanizer; using Boilerplate.Server.Api.Services; using Boilerplate.Shared.Dtos.Identity; using Boilerplate.Server.Api.Models.Identity; @@ -16,7 +16,9 @@ public partial class UserController : AppControllerBase, IUserController [AutoInject] private IUserStore<User> userStore = default!; - [AutoInject] private SmsService smsService = default!; + [AutoInject] private IUserEmailStore<User> userEmailStore = default!; + + [AutoInject] private PhoneService phoneService = default!; [AutoInject] private EmailService emailService = default!; @@ -42,23 +44,17 @@ public async Task<List<UserSessionDto>> GetUserSessions(CancellationToken cancel ?? throw new ResourceNotFoundException(); return user.Sessions - .OrderByDescending(s => s.RenewedOn) .Select(us => { var dto = us.Map(); - var lastSeenDateTime = us.RenewedOn ?? us.StartedOn; + dto.RenewedOn = us.RenewedOn ?? us.StartedOn; - dto.LastSeenOn = DateTimeOffset.UtcNow - lastSeenDateTime < TimeSpan.FromMinutes(5) - ? Localizer[nameof(AppStrings.Online)] - : DateTimeOffset.UtcNow - lastSeenDateTime < TimeSpan.FromMinutes(15) - ? Localizer[nameof(AppStrings.Recently)] - : lastSeenDateTime.Humanize(culture: CultureInfo.CurrentUICulture); - - dto.IsValid = DateTimeOffset.UtcNow - lastSeenDateTime < AppSettings.Identity.RefreshTokenExpiration; + dto.IsValid = DateTimeOffset.UtcNow - dto.RenewedOn < AppSettings.Identity.RefreshTokenExpiration; return dto; }) + .OrderByDescending(us => us.RenewedOn) .ToList(); } @@ -175,7 +171,7 @@ public async Task SendChangeEmailToken(SendEmailTokenRequestDto request, Cancell var link = new Uri( HttpContext.Request.GetWebClientUrl(), - $"{Urls.ProfilePage}?email={Uri.EscapeDataString(request.Email!)}&emailToken={Uri.EscapeDataString(token)}&culture={CultureInfo.CurrentUICulture.Name}"); + $"{Urls.SettingsPage}/{Urls.SettingsSections.Account}?email={Uri.EscapeDataString(request.Email!)}&emailToken={Uri.EscapeDataString(token)}&culture={CultureInfo.CurrentUICulture.Name}"); await emailService.SendEmailToken(user, request.Email!, token, link, cancellationToken); } @@ -199,7 +195,7 @@ public async Task ChangeEmail(ChangeEmailRequestDto request, CancellationToken c if (tokenIsValid is false) throw new BadRequestException(nameof(AppStrings.InvalidToken)); - await ((IUserEmailStore<User>)userStore).SetEmailAsync(user, request.Email, cancellationToken); + await userEmailStore.SetEmailAsync(user, request.Email, cancellationToken); var result = await userManager.UpdateAsync(user); if (result.Succeeded is false) @@ -216,6 +212,7 @@ public async Task ChangeEmail(ChangeEmailRequestDto request, CancellationToken c [HttpPost] public async Task SendChangePhoneNumberToken(SendPhoneTokenRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindByIdAsync(User.GetUserId().ToString()); var resendDelay = (DateTimeOffset.Now - user!.PhoneNumberTokenRequestedOn) - AppSettings.Identity.PhoneNumberTokenLifetime; @@ -231,12 +228,13 @@ public async Task SendChangePhoneNumberToken(SendPhoneTokenRequestDto request, C var token = await userManager.GenerateChangePhoneNumberTokenAsync(user!, request.PhoneNumber!); - await smsService.SendSms(Localizer[nameof(AppStrings.ChangePhoneNumberTokenSmsText), token], request.PhoneNumber!, cancellationToken); + await phoneService.SendSms(Localizer[nameof(AppStrings.ChangePhoneNumberTokenSmsText), token], request.PhoneNumber!, cancellationToken); } [HttpPost] public async Task ChangePhoneNumber(ChangePhoneNumberRequestDto request, CancellationToken cancellationToken) { + request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber); var user = await userManager.FindByIdAsync(User.GetUserId().ToString()); var expired = (DateTimeOffset.Now - user!.PhoneNumberTokenRequestedOn) > AppSettings.Identity.PhoneNumberTokenLifetime; @@ -306,7 +304,10 @@ public async Task<TwoFactorAuthResponseDto> TwoFactorAuth(TwoFactorAuthRequestDt var unformattedKey = await userManager.GetAuthenticatorKeyAsync(user); if (string.IsNullOrEmpty(unformattedKey)) { - await userManager.ResetAuthenticatorKeyAsync(user); + IUserAuthenticatorKeyStore<User> userAuthenticatorKeyStore = (IUserAuthenticatorKeyStore<User>)userStore; + await userAuthenticatorKeyStore.SetAuthenticatorKeyAsync(user, + userManager.GenerateNewAuthenticatorKey(), cancellationToken); + await userStore.UpdateAsync(user, cancellationToken); unformattedKey = await userManager.GetAuthenticatorKeyAsync(user); if (string.IsNullOrEmpty(unformattedKey)) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs index 141c9624ce..be48987b41 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs @@ -1,4 +1,9 @@ -using Boilerplate.Shared.Dtos.Products; +//+:cnd:noEmit +using Boilerplate.Server.Api.SignalR; +//#if (signalR == true) +using Microsoft.AspNetCore.SignalR; +//#endif +using Boilerplate.Shared.Dtos.Products; using Boilerplate.Shared.Controllers.Product; namespace Boilerplate.Server.Api.Controllers; @@ -6,6 +11,10 @@ namespace Boilerplate.Server.Api.Controllers; [ApiController, Route("api/[controller]/[action]")] public partial class ProductController : AppControllerBase, IProductController { + //#if (signalR == true) + [AutoInject] private IHubContext<AppHub> appHubContext = default!; + //#endif + [HttpGet, EnableQuery] public IQueryable<ProductDto> Get() { @@ -20,11 +29,8 @@ public async Task<PagedResult<ProductDto>> GetProducts(ODataQueryOptions<Product var totalCount = await query.LongCountAsync(cancellationToken); - if (odataQuery.Skip is not null) - query = query.Skip(odataQuery.Skip.Value); - - if (odataQuery.Top is not null) - query = query.Take(odataQuery.Top.Value); + query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip!.Value) + .TakeIf(odataQuery.Top is not null, odataQuery.Top!.Value); return new PagedResult<ProductDto>(await query.ToArrayAsync(cancellationToken), totalCount); } @@ -49,33 +55,46 @@ public async Task<ProductDto> Create(ProductDto dto, CancellationToken cancellat await DbContext.SaveChangesAsync(cancellationToken); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + return entityToAdd.Map(); } [HttpPut] public async Task<ProductDto> Update(ProductDto dto, CancellationToken cancellationToken) { - var entityToUpdate = await DbContext.Products.FirstOrDefaultAsync(t => t.Id == dto.Id, cancellationToken); + var entityToUpdate = dto.Map(); - if (entityToUpdate is null) - throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ProductCouldNotBeFound)]); - - dto.Patch(entityToUpdate); + DbContext.Update(entityToUpdate); await DbContext.SaveChangesAsync(cancellationToken); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + return entityToUpdate.Map(); } - [HttpDelete("{id}")] - public async Task Delete(Guid id, CancellationToken cancellationToken) + [HttpDelete("{id}/{concurrencyStamp}")] + public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) { - DbContext.Products.Remove(new() { Id = id }); + DbContext.Products.Remove(new() { Id = id, ConcurrencyStamp = Convert.FromBase64String(Uri.UnescapeDataString(concurrencyStamp)) }); - var affectedRows = await DbContext.SaveChangesAsync(cancellationToken); + await DbContext.SaveChangesAsync(cancellationToken); - if (affectedRows < 1) - throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ProductCouldNotBeFound)]); + //#if (signalR == true) + await PublishDashboardDataChanged(cancellationToken); + //#endif + } + + //#if (signalR == true) + private async Task PublishDashboardDataChanged(CancellationToken cancellationToken) + { + await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, cancellationToken); } + //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/PushNotification/PushNotificationController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/PushNotification/PushNotificationController.cs new file mode 100644 index 0000000000..081138ce2c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/PushNotification/PushNotificationController.cs @@ -0,0 +1,33 @@ +//-:cnd:noEmit +using Boilerplate.Server.Api.Services; +using Boilerplate.Shared.Dtos.PushNotification; +using Boilerplate.Shared.Controllers.PushNotification; + +namespace Boilerplate.Server.Api.Controllers.PushNotification; + +[Route("api/[controller]/[action]")] +[ApiController, AllowAnonymous] +public partial class PushNotificationController : AppControllerBase, IPushNotificationController +{ + [AutoInject] PushNotificationService pushNotificationService = default!; + + [HttpPost] + public async Task RegisterDevice([Required] DeviceInstallationDto deviceInstallation, CancellationToken cancellationToken) + { + await pushNotificationService.RegisterDevice(deviceInstallation, cancellationToken); + } + + [HttpPost("{deviceId}")] + public async Task DeregisterDevice([Required] string deviceId, CancellationToken cancellationToken) + { + await pushNotificationService.DeregisterDevice(deviceId, cancellationToken); + } + +#if Development // This action is for testing purposes only. + [HttpPost] + public async Task RequestPush([FromQuery] string? title = null, [FromQuery] string? message = null, [FromQuery] string? action = null, CancellationToken cancellationToken = default) + { + await pushNotificationService.RequestPush(title, message, action, null, cancellationToken); + } +#endif +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs index e08a8df759..cb6ca4f71c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs @@ -1,5 +1,4 @@ using Boilerplate.Shared.Dtos.Todo; -using Boilerplate.Server.Api.Models.Todo; using Boilerplate.Shared.Controllers.Todo; namespace Boilerplate.Server.Api.Controllers.Todo; @@ -24,11 +23,8 @@ public async Task<PagedResult<TodoItemDto>> GetTodoItems(ODataQueryOptions<TodoI var totalCount = await query.LongCountAsync(cancellationToken); - if (odataQuery.Skip is not null) - query = query.Skip(odataQuery.Skip.Value); - - if (odataQuery.Top is not null) - query = query.Take(odataQuery.Top.Value); + query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip!.Value) + .TakeIf(odataQuery.Top is not null, odataQuery.Top!.Value); return new PagedResult<TodoItemDto>(await query.ToArrayAsync(cancellationToken), totalCount); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs index e79b6c7060..081ce477e8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs @@ -9,31 +9,41 @@ using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Boilerplate.Server.Api.Models.Identity; using Boilerplate.Server.Api.Data.Configurations; +//#if (notification == true) +using Boilerplate.Server.Api.Models.PushNotification; +//#endif namespace Boilerplate.Server.Api.Data; public partial class AppDbContext(DbContextOptions<AppDbContext> options) : IdentityDbContext<User, Role, Guid>(options), IDataProtectionKeyContext { - public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } + public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = default!; //#if (sample == "Todo") - public DbSet<TodoItem> TodoItems { get; set; } + public DbSet<TodoItem> TodoItems { get; set; } = default!; //#elif (sample == "Admin") - public DbSet<Category> Categories { get; set; } - public DbSet<Product> Products { get; set; } + public DbSet<Category> Categories { get; set; } = default!; + public DbSet<Product> Products { get; set; } = default!; + //#endif + //#if (notification == true) + public DbSet<DeviceInstallation> DeviceInstallations { get; set; } = default!; //#endif - protected override void OnModelCreating(ModelBuilder builder) + protected override void OnModelCreating(ModelBuilder modelBuilder) { - base.OnModelCreating(builder); + base.OnModelCreating(modelBuilder); - builder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); + modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); //#if (database != "Cosmos") - ConfigureIdentityTableNames(builder); + ConfigureIdentityTableNames(modelBuilder); //#else - ConfigureContainers(builder); + ConfigureContainers(modelBuilder); + //#endif + + //#if (database != "Sqlite" && database != "Cosmos") + ConcurrencyStamp(modelBuilder); //#endif } @@ -177,6 +187,49 @@ private void ConfigureContainers(ModelBuilder builder) builder.Entity<Product>() .ToContainer("Products").HasPartitionKey(e => e.CategoryId); //#endif + + //#if (notification == true) + builder.Entity<DeviceInstallation>() + .ToContainer("DeviceInstallations").HasPartitionKey(e => e.Platform); + //#endif + } + //#endif + + //#if (database != "Sqlite" && database != "Cosmos") + private void ConcurrencyStamp(ModelBuilder modelBuilder) + { + //#if (IsInsideProjectTemplate == true) + if (Database.ProviderName!.EndsWith("Sqlite", StringComparison.InvariantCulture) || + Database.ProviderName!.EndsWith("Cosmos", StringComparison.InvariantCulture)) + return; + //#endif + + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + foreach (var property in entityType.GetProperties() + .Where(p => p.Name is "ConcurrencyStamp")) + { + var builder = new PropertyBuilder(property); + builder.IsConcurrencyToken() + .IsRowVersion(); + + //#if (IsInsideProjectTemplate == true) + if (Database.ProviderName.EndsWith("PostgreSQL", StringComparison.InvariantCulture)) + { + //#endif + //#if (database == "PostgreSQL") + if (property.ClrType == typeof(byte[])) + { + builder.HasConversion(new ValueConverter<byte[], uint>( + v => BitConverter.ToUInt32(v, 0), + v => BitConverter.GetBytes(v))); + } + //#endif + //#if (IsInsideProjectTemplate == true) + } + //#endif + } + } } //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs index 9e099d9f71..aff3bd6c55 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs @@ -6,12 +6,13 @@ public partial class CategoryConfiguration : IEntityTypeConfiguration<Category> { public void Configure(EntityTypeBuilder<Category> builder) { + var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; builder.HasData( - new() { Id = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), Name = "Ford", Color = "#FFCD56" }, - new() { Id = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), Name = "Nissan", Color = "#FF6384" }, - new() { Id = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), Name = "Benz", Color = "#4BC0C0" }, - new() { Id = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), Name = "BMW", Color = "#FF9124" }, - new() { Id = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), Name = "Tesla", Color = "#2B88D8" }); + new() { Id = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), Name = "Ford", Color = "#FFCD56", ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), Name = "Nissan", Color = "#FF6384", ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), Name = "Benz", Color = "#4BC0C0", ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), Name = "BMW", Color = "#FF9124", ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), Name = "Tesla", Color = "#2B88D8", ConcurrencyStamp = defaultConcurrencyStamp }); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Identity/UserConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Identity/UserConfiguration.cs index ab26d067fd..ad71787c83 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Identity/UserConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Identity/UserConfiguration.cs @@ -1,5 +1,4 @@ //+:cnd:noEmit -using System.Text.Json; using Boilerplate.Server.Api.Models.Identity; namespace Boilerplate.Server.Api.Data.Configurations.Identity; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs index c36a9eee2b..2f134d2d9c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs @@ -6,36 +6,37 @@ public partial class ProductConfiguration : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { + var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; DateTimeOffset baseDate = DateTimeOffset.Parse("2022-07-12", styles: DateTimeStyles.AssumeUniversal); builder.HasData( - new() { Id = Guid.Parse("9a59dda2-7b12-4cc1-9658-d2586eef91d4"), Name = "Mustang", Price = 27155, Description = "The Ford Mustang is ranked #1 in Sports Cars", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845") }, - new() { Id = Guid.Parse("a42914e2-92da-4f0b-aab0-b9572c9671b4"), Name = "GT", Price = 500000, Description = "The Ford GT is a mid-engine two-seater sports car manufactured and marketed by American automobile manufacturer", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845") }, - new() { Id = Guid.Parse("f75325c8-a213-470b-ab93-4677ca4caeef"), Name = "Ranger", Price = 25000, Description = "Ford Ranger is a nameplate that has been used on multiple model lines of pickup trucks sold by Ford worldwide.", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845") }, - new() { Id = Guid.Parse("43a82ec1-aab6-445f-83af-a85028417cf7"), Name = "Raptor", Price = 53205, Description = "Raptor is a SCORE off-road trophy truck living in a asphalt world", CreatedOn = baseDate.AddDays(-30), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845") }, - new() { Id = Guid.Parse("f01b32bb-eccd-43be-aaf3-3c788a7d7558"), Name = "Maverick", Price = 22470, Description = "The Ford Maverick is a compact pickup truck produced by Ford Motor Company.", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845") }, - - new() { Id = Guid.Parse("d53bb159-f4f9-493a-b4dc-215fd765ca25"), Name = "Roadster", Price = 42800, Description = "A powerful convertible sports car", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c") }, - new() { Id = Guid.Parse("74bb268f-18cf-45ec-9f2f-30b34b18fb3c"), Name = "Altima", Price = 24550, Description = "A perfectly adequate family sedan with sharp looks", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c") }, - new() { Id = Guid.Parse("eb787e1a-7ba8-4708-924b-9f7964fa0f64"), Name = "GT-R", Price = 113540, Description = "Legendary supercar with AWD, 4 seats, a powerful V6 engine and the latest tech", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c") }, - new() { Id = Guid.Parse("362a6638-0031-485d-825f-e8aeae63a334"), Name = "Juke", Price = 28100, Description = "A new smart SUV", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c") }, - - new() { Id = Guid.Parse("8629931e-e26e-4885-b561-e447197d4b69"), Name = "H247", Price = 54950, Description = "", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08") }, - new() { Id = Guid.Parse("a1c1987d-ee6c-41ad-9647-18de4504303a"), Name = "V297", Price = 103360, Description = "", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08") }, - new() { Id = Guid.Parse("59eea437-bdf2-4c11-b262-06643b253288"), Name = "R50", Price = 2000000, Description = "", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08") }, - - new() { Id = Guid.Parse("01d223a3-182d-406a-9722-19dab083f96e"), Name = "M550i", Price = 77790, Description = "", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("64a2616f-3af6-4248-86cf-4a605095a644"), Name = "540i", Price = 60945, Description = "", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("ac50dc29-4b7e-4d4d-b23a-4227d91f2bb0"), Name = "530e", Price = 56545, Description = "", CreatedOn = baseDate.AddDays(-20), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("fb41cc51-9abd-4b45-b0d9-ea8f565ec502"), Name = "530i", Price = 55195, Description = "", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("e159b1ad-12aa-4e02-a39b-d5e4a32eaf99"), Name = "M850i", Price = 100045, Description = "", CreatedOn = baseDate.AddDays(-30), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("4d9cb0f4-1f32-45d5-8c84-d7f15bc569d5"), Name = "X7", Price = 77980, Description = "", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - new() { Id = Guid.Parse("1b22319e-0a58-471e-82b6-75cd8b9d98e1"), Name = "IX", Price = 87000, Description = "", CreatedOn = baseDate.AddDays(-40), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4") }, - - new() { Id = Guid.Parse("96c73b9c-03df-4f70-ac8d-75c32b89881a"), Name = "Model 3", Price = 61990, Description = "rapid acceleration and dynamic handling", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d") }, - new() { Id = Guid.Parse("840ba759-bde9-4821-b49b-c981c082bb96"), Name = "Model S", Price = 135000, Description = "finishes near the top of our luxury electric car rankings.", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d") }, - new() { Id = Guid.Parse("840e113b-5074-4b1c-86bd-e9affb659412"), Name = "Model X", Price = 138890, Description = "Heart-pumping acceleration, long drive range", CreatedOn = baseDate.AddDays(-20), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d") }, - new() { Id = Guid.Parse("b2db9074-a0a9-4054-87e2-206b7a55c793"), Name = "Model Y", Price = 67790, Description = "extensive driving range, lots of standard safety features", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d") } + new() { Id = Guid.Parse("9a59dda2-7b12-4cc1-9658-d2586eef91d4"), Name = "Mustang", Price = 27155, Description = "The Ford Mustang is ranked #1 in Sports Cars", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("a42914e2-92da-4f0b-aab0-b9572c9671b4"), Name = "GT", Price = 500000, Description = "The Ford GT is a mid-engine two-seater sports car manufactured and marketed by American automobile manufacturer", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("f75325c8-a213-470b-ab93-4677ca4caeef"), Name = "Ranger", Price = 25000, Description = "Ford Ranger is a nameplate that has been used on multiple model lines of pickup trucks sold by Ford worldwide.", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("43a82ec1-aab6-445f-83af-a85028417cf7"), Name = "Raptor", Price = 53205, Description = "Raptor is a SCORE off-road trophy truck living in a asphalt world", CreatedOn = baseDate.AddDays(-30), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("f01b32bb-eccd-43be-aaf3-3c788a7d7558"), Name = "Maverick", Price = 22470, Description = "The Ford Maverick is a compact pickup truck produced by Ford Motor Company.", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), ConcurrencyStamp = defaultConcurrencyStamp }, + + new() { Id = Guid.Parse("d53bb159-f4f9-493a-b4dc-215fd765ca25"), Name = "Roadster", Price = 42800, Description = "A powerful convertible sports car", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("74bb268f-18cf-45ec-9f2f-30b34b18fb3c"), Name = "Altima", Price = 24550, Description = "A perfectly adequate family sedan with sharp looks", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("eb787e1a-7ba8-4708-924b-9f7964fa0f64"), Name = "GT-R", Price = 113540, Description = "Legendary supercar with AWD, 4 seats, a powerful V6 engine and the latest tech", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("362a6638-0031-485d-825f-e8aeae63a334"), Name = "Juke", Price = 28100, Description = "A new smart SUV", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), ConcurrencyStamp = defaultConcurrencyStamp }, + + new() { Id = Guid.Parse("8629931e-e26e-4885-b561-e447197d4b69"), Name = "H247", Price = 54950, Description = "", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("a1c1987d-ee6c-41ad-9647-18de4504303a"), Name = "V297", Price = 103360, Description = "", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("59eea437-bdf2-4c11-b262-06643b253288"), Name = "R50", Price = 2000000, Description = "", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), ConcurrencyStamp = defaultConcurrencyStamp }, + + new() { Id = Guid.Parse("01d223a3-182d-406a-9722-19dab083f96e"), Name = "M550i", Price = 77790, Description = "", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("64a2616f-3af6-4248-86cf-4a605095a644"), Name = "540i", Price = 60945, Description = "", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("ac50dc29-4b7e-4d4d-b23a-4227d91f2bb0"), Name = "530e", Price = 56545, Description = "", CreatedOn = baseDate.AddDays(-20), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("fb41cc51-9abd-4b45-b0d9-ea8f565ec502"), Name = "530i", Price = 55195, Description = "", CreatedOn = baseDate.AddDays(-25), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("e159b1ad-12aa-4e02-a39b-d5e4a32eaf99"), Name = "M850i", Price = 100045, Description = "", CreatedOn = baseDate.AddDays(-30), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("4d9cb0f4-1f32-45d5-8c84-d7f15bc569d5"), Name = "X7", Price = 77980, Description = "", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("1b22319e-0a58-471e-82b6-75cd8b9d98e1"), Name = "IX", Price = 87000, Description = "", CreatedOn = baseDate.AddDays(-40), CategoryId = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), ConcurrencyStamp = defaultConcurrencyStamp }, + + new() { Id = Guid.Parse("96c73b9c-03df-4f70-ac8d-75c32b89881a"), Name = "Model 3", Price = 61990, Description = "rapid acceleration and dynamic handling", CreatedOn = baseDate.AddDays(-10), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("840ba759-bde9-4821-b49b-c981c082bb96"), Name = "Model S", Price = 135000, Description = "finishes near the top of our luxury electric car rankings.", CreatedOn = baseDate.AddDays(-15), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("840e113b-5074-4b1c-86bd-e9affb659412"), Name = "Model X", Price = 138890, Description = "Heart-pumping acceleration, long drive range", CreatedOn = baseDate.AddDays(-20), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), ConcurrencyStamp = defaultConcurrencyStamp }, + new() { Id = Guid.Parse("b2db9074-a0a9-4054-87e2-206b7a55c793"), Name = "Model Y", Price = 67790, Description = "extensive driving range, lots of standard safety features", CreatedOn = baseDate.AddDays(-35), CategoryId = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), ConcurrencyStamp = defaultConcurrencyStamp } ); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.Designer.cs new file mode 100644 index 0000000000..d7123ec841 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.Designer.cs @@ -0,0 +1,821 @@ +// <auto-generated /> +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable disable + +namespace Boilerplate.Server.Api.Data.Migrations; + +[DbContext(typeof(AppDbContext))] +[Migration("20241107182721_InitialMigration")] +partial class InitialMigration +{ + /// <inheritdoc /> + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.10"); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Categories.Category", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<string>("Color") + .HasColumnType("TEXT"); + + b.Property<byte[]>("ConcurrencyStamp") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property<string>("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b + .HasAnnotation("Cosmos:ContainerName", "Categories") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + + b.HasData( + new + { + Id = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + Color = "#FFCD56", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Ford" + }, + new + { + Id = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + Color = "#FF6384", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Nissan" + }, + new + { + Id = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + Color = "#4BC0C0", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Benz" + }, + new + { + Id = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + Color = "#FF9124", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "BMW" + }, + new + { + Id = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + Color = "#2B88D8", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Tesla" + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Identity.Role", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasAnnotation("Cosmos:PropertyName", "_etag"); + + b.Property<string>("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property<string>("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("Roles", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "Roles") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Identity.User", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<int>("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property<long?>("BirthDate") + .HasColumnType("INTEGER"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasAnnotation("Cosmos:PropertyName", "_etag"); + + b.Property<string>("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<bool>("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property<long?>("EmailTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("FullName") + .HasColumnType("TEXT"); + + b.Property<int?>("Gender") + .HasColumnType("INTEGER"); + + b.Property<bool>("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property<long?>("LockoutEnd") + .HasColumnType("INTEGER"); + + b.Property<string>("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<string>("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<long?>("OtpRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("PasswordHash") + .HasColumnType("TEXT"); + + b.Property<string>("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property<bool>("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property<long?>("PhoneNumberTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("ProfileImageName") + .HasColumnType("TEXT"); + + b.Property<long?>("ResetPasswordTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property<string>("Sessions") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<bool>("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property<long?>("TwoFactorTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("Users", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "Users") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + + b.HasData( + new + { + Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), + AccessFailedCount = 0, + BirthDate = 1306790461440000000L, + ConcurrencyStamp = "315e1a26-5b3a-4544-8e91-2760cd28e231", + Email = "test@bitplatform.dev", + EmailConfirmed = true, + EmailTokenRequestedOn = 1306790461440000000L, + FullName = "Boilerplate test account", + Gender = 0, + LockoutEnabled = true, + NormalizedEmail = "TEST@BITPLATFORM.DEV", + NormalizedUserName = "TEST", + PasswordHash = "AQAAAAIAAYagAAAAEP0v3wxkdWtMkHA3Pp5/JfS+42/Qto9G05p2mta6dncSK37hPxEHa3PGE4aqN30Aag==", + PhoneNumber = "+31684207362", + PhoneNumberConfirmed = true, + SecurityStamp = "959ff4a9-4b07-4cc1-8141-c5fc033daf83", + Sessions = "[]", + TwoFactorEnabled = false, + UserName = "test" + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Products.Product", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<Guid>("CategoryId") + .HasColumnType("TEXT"); + + b.Property<byte[]>("ConcurrencyStamp") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property<long>("CreatedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("Description") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property<decimal>("Price") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products"); + + b + .HasAnnotation("Cosmos:ContainerName", "Products") + .HasAnnotation("Cosmos:PartitionKeyName", "CategoryId"); + + b.HasData( + new + { + Id = new Guid("9a59dda2-7b12-4cc1-9658-d2586eef91d4"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "The Ford Mustang is ranked #1 in Sports Cars", + Name = "Mustang", + Price = 27155m + }, + new + { + Id = new Guid("a42914e2-92da-4f0b-aab0-b9572c9671b4"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "The Ford GT is a mid-engine two-seater sports car manufactured and marketed by American automobile manufacturer", + Name = "GT", + Price = 500000m + }, + new + { + Id = new Guid("f75325c8-a213-470b-ab93-4677ca4caeef"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "Ford Ranger is a nameplate that has been used on multiple model lines of pickup trucks sold by Ford worldwide.", + Name = "Ranger", + Price = 25000m + }, + new + { + Id = new Guid("43a82ec1-aab6-445f-83af-a85028417cf7"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306431258624000000L, + Description = "Raptor is a SCORE off-road trophy truck living in a asphalt world", + Name = "Raptor", + Price = 53205m + }, + new + { + Id = new Guid("f01b32bb-eccd-43be-aaf3-3c788a7d7558"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "The Ford Maverick is a compact pickup truck produced by Ford Motor Company.", + Name = "Maverick", + Price = 22470m + }, + new + { + Id = new Guid("d53bb159-f4f9-493a-b4dc-215fd765ca25"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "A powerful convertible sports car", + Name = "Roadster", + Price = 42800m + }, + new + { + Id = new Guid("74bb268f-18cf-45ec-9f2f-30b34b18fb3c"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "A perfectly adequate family sedan with sharp looks", + Name = "Altima", + Price = 24550m + }, + new + { + Id = new Guid("eb787e1a-7ba8-4708-924b-9f7964fa0f64"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "Legendary supercar with AWD, 4 seats, a powerful V6 engine and the latest tech", + Name = "GT-R", + Price = 113540m + }, + new + { + Id = new Guid("362a6638-0031-485d-825f-e8aeae63a334"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "A new smart SUV", + Name = "Juke", + Price = 28100m + }, + new + { + Id = new Guid("8629931e-e26e-4885-b561-e447197d4b69"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "", + Name = "H247", + Price = 54950m + }, + new + { + Id = new Guid("a1c1987d-ee6c-41ad-9647-18de4504303a"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "", + Name = "V297", + Price = 103360m + }, + new + { + Id = new Guid("59eea437-bdf2-4c11-b262-06643b253288"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "", + Name = "R50", + Price = 2000000m + }, + new + { + Id = new Guid("01d223a3-182d-406a-9722-19dab083f96e"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "", + Name = "M550i", + Price = 77790m + }, + new + { + Id = new Guid("64a2616f-3af6-4248-86cf-4a605095a644"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "", + Name = "540i", + Price = 60945m + }, + new + { + Id = new Guid("ac50dc29-4b7e-4d4d-b23a-4227d91f2bb0"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306448953344000000L, + Description = "", + Name = "530e", + Price = 56545m + }, + new + { + Id = new Guid("fb41cc51-9abd-4b45-b0d9-ea8f565ec502"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "", + Name = "530i", + Price = 55195m + }, + new + { + Id = new Guid("e159b1ad-12aa-4e02-a39b-d5e4a32eaf99"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306431258624000000L, + Description = "", + Name = "M850i", + Price = 100045m + }, + new + { + Id = new Guid("4d9cb0f4-1f32-45d5-8c84-d7f15bc569d5"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "", + Name = "X7", + Price = 77980m + }, + new + { + Id = new Guid("1b22319e-0a58-471e-82b6-75cd8b9d98e1"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306413563904000000L, + Description = "", + Name = "IX", + Price = 87000m + }, + new + { + Id = new Guid("96c73b9c-03df-4f70-ac8d-75c32b89881a"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "rapid acceleration and dynamic handling", + Name = "Model 3", + Price = 61990m + }, + new + { + Id = new Guid("840ba759-bde9-4821-b49b-c981c082bb96"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "finishes near the top of our luxury electric car rankings.", + Name = "Model S", + Price = 135000m + }, + new + { + Id = new Guid("840e113b-5074-4b1c-86bd-e9affb659412"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306448953344000000L, + Description = "Heart-pumping acceleration, long drive range", + Name = "Model X", + Price = 138890m + }, + new + { + Id = new Guid("b2db9074-a0a9-4054-87e2-206b7a55c793"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "extensive driving range, lots of standard safety features", + Name = "Model Y", + Price = 67790m + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.PushNotification.DeviceInstallation", b => + { + b.Property<string>("InstallationId") + .HasColumnType("TEXT"); + + b.Property<string>("Auth") + .HasColumnType("TEXT"); + + b.Property<string>("Endpoint") + .HasColumnType("TEXT"); + + b.Property<long>("ExpirationTime") + .HasColumnType("INTEGER"); + + b.Property<string>("P256dh") + .HasColumnType("TEXT"); + + b.Property<string>("Platform") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("PushChannel") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("Tags") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid?>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InstallationId"); + + b.HasIndex("UserId"); + + b.ToTable("DeviceInstallations"); + + b + .HasAnnotation("Cosmos:ContainerName", "DeviceInstallations") + .HasAnnotation("Cosmos:PartitionKeyName", "Platform"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Todo.TodoItem", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<long>("Date") + .HasColumnType("INTEGER"); + + b.Property<bool>("IsDone") + .HasColumnType("INTEGER"); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("TodoItems"); + + b + .HasAnnotation("Cosmos:ContainerName", "TodoItems") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("FriendlyName") + .HasColumnType("TEXT"); + + b.Property<string>("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + + b + .HasAnnotation("Cosmos:ContainerName", "DataProtectionKeys") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ClaimType") + .HasColumnType("TEXT"); + + b.Property<string>("ClaimValue") + .HasColumnType("TEXT"); + + b.Property<Guid>("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "RoleClaims") + .HasAnnotation("Cosmos:PartitionKeyName", "RoleId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ClaimType") + .HasColumnType("TEXT"); + + b.Property<string>("ClaimValue") + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaims", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserClaims") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.Property<string>("LoginProvider") + .HasColumnType("TEXT"); + + b.Property<string>("ProviderKey") + .HasColumnType("TEXT"); + + b.Property<string>("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogins", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserLogins") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<Guid>("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserRoles") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<string>("LoginProvider") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .HasColumnType("TEXT"); + + b.Property<string>("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserTokens") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Products.Product", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Categories.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.PushNotification.DeviceInstallation", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Todo.TodoItem", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Categories.Category", b => + { + b.Navigation("Products"); + }); +#pragma warning restore 612, 618 + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.cs new file mode 100644 index 0000000000..5530558a95 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20241107182721_InitialMigration.cs @@ -0,0 +1,417 @@ +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Boilerplate.Server.Api.Data.Migrations; + +/// <inheritdoc /> +public partial class InitialMigration : Migration +{ + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + Name = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false), + Color = table.Column<string>(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column<byte[]>(type: "BLOB", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "DataProtectionKeys", + columns: table => new + { + Id = table.Column<int>(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FriendlyName = table.Column<string>(type: "TEXT", nullable: true), + Xml = table.Column<string>(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + Name = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true), + NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column<string>(type: "TEXT", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + FullName = table.Column<string>(type: "TEXT", nullable: true), + Gender = table.Column<int>(type: "INTEGER", nullable: true), + BirthDate = table.Column<long>(type: "INTEGER", nullable: true), + ProfileImageName = table.Column<string>(type: "TEXT", nullable: true), + Sessions = table.Column<string>(type: "TEXT", nullable: false), + EmailTokenRequestedOn = table.Column<long>(type: "INTEGER", nullable: true), + PhoneNumberTokenRequestedOn = table.Column<long>(type: "INTEGER", nullable: true), + ResetPasswordTokenRequestedOn = table.Column<long>(type: "INTEGER", nullable: true), + TwoFactorTokenRequestedOn = table.Column<long>(type: "INTEGER", nullable: true), + OtpRequestedOn = table.Column<long>(type: "INTEGER", nullable: true), + UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false), + PasswordHash = table.Column<string>(type: "TEXT", nullable: true), + SecurityStamp = table.Column<string>(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column<string>(type: "TEXT", rowVersion: true, nullable: true), + PhoneNumber = table.Column<string>(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column<bool>(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column<bool>(type: "INTEGER", nullable: false), + LockoutEnd = table.Column<long>(type: "INTEGER", nullable: true), + LockoutEnabled = table.Column<bool>(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + Name = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false), + Price = table.Column<decimal>(type: "TEXT", nullable: false), + Description = table.Column<string>(type: "TEXT", maxLength: 512, nullable: true), + CreatedOn = table.Column<long>(type: "INTEGER", nullable: false), + CategoryId = table.Column<Guid>(type: "TEXT", nullable: false), + ConcurrencyStamp = table.Column<byte[]>(type: "BLOB", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + table.ForeignKey( + name: "FK_Products_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "RoleClaims", + columns: table => new + { + Id = table.Column<int>(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column<Guid>(type: "TEXT", nullable: false), + ClaimType = table.Column<string>(type: "TEXT", nullable: true), + ClaimValue = table.Column<string>(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_RoleClaims_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "DeviceInstallations", + columns: table => new + { + InstallationId = table.Column<string>(type: "TEXT", nullable: false), + Platform = table.Column<string>(type: "TEXT", nullable: false), + PushChannel = table.Column<string>(type: "TEXT", nullable: false), + P256dh = table.Column<string>(type: "TEXT", nullable: true), + Auth = table.Column<string>(type: "TEXT", nullable: true), + Endpoint = table.Column<string>(type: "TEXT", nullable: true), + UserId = table.Column<Guid>(type: "TEXT", nullable: true), + Tags = table.Column<string>(type: "TEXT", nullable: false), + ExpirationTime = table.Column<long>(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DeviceInstallations", x => x.InstallationId); + table.ForeignKey( + name: "FK_DeviceInstallations_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "TodoItems", + columns: table => new + { + Id = table.Column<Guid>(type: "TEXT", nullable: false), + Title = table.Column<string>(type: "TEXT", nullable: false), + Date = table.Column<long>(type: "INTEGER", nullable: false), + IsDone = table.Column<bool>(type: "INTEGER", nullable: false), + UserId = table.Column<Guid>(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TodoItems", x => x.Id); + table.ForeignKey( + name: "FK_TodoItems_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "UserClaims", + columns: table => new + { + Id = table.Column<int>(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column<Guid>(type: "TEXT", nullable: false), + ClaimType = table.Column<string>(type: "TEXT", nullable: true), + ClaimValue = table.Column<string>(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserClaims", x => x.Id); + table.ForeignKey( + name: "FK_UserClaims_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "UserLogins", + columns: table => new + { + LoginProvider = table.Column<string>(type: "TEXT", nullable: false), + ProviderKey = table.Column<string>(type: "TEXT", nullable: false), + ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true), + UserId = table.Column<Guid>(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_UserLogins_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "UserRoles", + columns: table => new + { + UserId = table.Column<Guid>(type: "TEXT", nullable: false), + RoleId = table.Column<Guid>(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_UserRoles_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UserRoles_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "UserTokens", + columns: table => new + { + UserId = table.Column<Guid>(type: "TEXT", nullable: false), + LoginProvider = table.Column<string>(type: "TEXT", nullable: false), + Name = table.Column<string>(type: "TEXT", nullable: false), + Value = table.Column<string>(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_UserTokens_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Categories", + columns: new[] { "Id", "Color", "ConcurrencyStamp", "Name" }, + values: new object[,] + { + { new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), "#FFCD56", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, "Ford" }, + { new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), "#FF6384", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, "Nissan" }, + { new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), "#4BC0C0", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, "Benz" }, + { new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), "#2B88D8", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, "Tesla" }, + { new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), "#FF9124", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, "BMW" } + }); + + migrationBuilder.InsertData( + table: "Users", + columns: new[] { "Id", "AccessFailedCount", "BirthDate", "Email", "EmailConfirmed", "EmailTokenRequestedOn", "FullName", "Gender", "LockoutEnabled", "LockoutEnd", "NormalizedEmail", "NormalizedUserName", "OtpRequestedOn", "PasswordHash", "PhoneNumber", "PhoneNumberConfirmed", "PhoneNumberTokenRequestedOn", "ProfileImageName", "ResetPasswordTokenRequestedOn", "SecurityStamp", "Sessions", "TwoFactorEnabled", "TwoFactorTokenRequestedOn", "UserName" }, + values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 0, 1306790461440000000L, "test@bitplatform.dev", true, 1306790461440000000L, "Boilerplate test account", 0, true, null, "TEST@BITPLATFORM.DEV", "TEST", null, "AQAAAAIAAYagAAAAEP0v3wxkdWtMkHA3Pp5/JfS+42/Qto9G05p2mta6dncSK37hPxEHa3PGE4aqN30Aag==", "+31684207362", true, null, null, null, "959ff4a9-4b07-4cc1-8141-c5fc033daf83", "[]", false, null, "test" }); + + migrationBuilder.InsertData( + table: "Products", + columns: new[] { "Id", "CategoryId", "ConcurrencyStamp", "CreatedOn", "Description", "Name", "Price" }, + values: new object[,] + { + { new Guid("01d223a3-182d-406a-9722-19dab083f96e"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306466648064000000L, "", "M550i", 77790m }, + { new Guid("1b22319e-0a58-471e-82b6-75cd8b9d98e1"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306413563904000000L, "", "IX", 87000m }, + { new Guid("362a6638-0031-485d-825f-e8aeae63a334"), new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306422411264000000L, "A new smart SUV", "Juke", 28100m }, + { new Guid("43a82ec1-aab6-445f-83af-a85028417cf7"), new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306431258624000000L, "Raptor is a SCORE off-road trophy truck living in a asphalt world", "Raptor", 53205m }, + { new Guid("4d9cb0f4-1f32-45d5-8c84-d7f15bc569d5"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306422411264000000L, "", "X7", 77980m }, + { new Guid("59eea437-bdf2-4c11-b262-06643b253288"), new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306422411264000000L, "", "R50", 2000000m }, + { new Guid("64a2616f-3af6-4248-86cf-4a605095a644"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306457800704000000L, "", "540i", 60945m }, + { new Guid("74bb268f-18cf-45ec-9f2f-30b34b18fb3c"), new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306457800704000000L, "A perfectly adequate family sedan with sharp looks", "Altima", 24550m }, + { new Guid("840ba759-bde9-4821-b49b-c981c082bb96"), new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306457800704000000L, "finishes near the top of our luxury electric car rankings.", "Model S", 135000m }, + { new Guid("840e113b-5074-4b1c-86bd-e9affb659412"), new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306448953344000000L, "Heart-pumping acceleration, long drive range", "Model X", 138890m }, + { new Guid("8629931e-e26e-4885-b561-e447197d4b69"), new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306466648064000000L, "", "H247", 54950m }, + { new Guid("96c73b9c-03df-4f70-ac8d-75c32b89881a"), new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306466648064000000L, "rapid acceleration and dynamic handling", "Model 3", 61990m }, + { new Guid("9a59dda2-7b12-4cc1-9658-d2586eef91d4"), new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306466648064000000L, "The Ford Mustang is ranked #1 in Sports Cars", "Mustang", 27155m }, + { new Guid("a1c1987d-ee6c-41ad-9647-18de4504303a"), new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306457800704000000L, "", "V297", 103360m }, + { new Guid("a42914e2-92da-4f0b-aab0-b9572c9671b4"), new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306457800704000000L, "The Ford GT is a mid-engine two-seater sports car manufactured and marketed by American automobile manufacturer", "GT", 500000m }, + { new Guid("ac50dc29-4b7e-4d4d-b23a-4227d91f2bb0"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306448953344000000L, "", "530e", 56545m }, + { new Guid("b2db9074-a0a9-4054-87e2-206b7a55c793"), new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306422411264000000L, "extensive driving range, lots of standard safety features", "Model Y", 67790m }, + { new Guid("d53bb159-f4f9-493a-b4dc-215fd765ca25"), new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306466648064000000L, "A powerful convertible sports car", "Roadster", 42800m }, + { new Guid("e159b1ad-12aa-4e02-a39b-d5e4a32eaf99"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306431258624000000L, "", "M850i", 100045m }, + { new Guid("eb787e1a-7ba8-4708-924b-9f7964fa0f64"), new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306440105984000000L, "Legendary supercar with AWD, 4 seats, a powerful V6 engine and the latest tech", "GT-R", 113540m }, + { new Guid("f01b32bb-eccd-43be-aaf3-3c788a7d7558"), new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306422411264000000L, "The Ford Maverick is a compact pickup truck produced by Ford Motor Company.", "Maverick", 22470m }, + { new Guid("f75325c8-a213-470b-ab93-4677ca4caeef"), new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306440105984000000L, "Ford Ranger is a nameplate that has been used on multiple model lines of pickup trucks sold by Ford worldwide.", "Ranger", 25000m }, + { new Guid("fb41cc51-9abd-4b45-b0d9-ea8f565ec502"), new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 1306440105984000000L, "", "530i", 55195m } + }); + + migrationBuilder.CreateIndex( + name: "IX_DeviceInstallations_UserId", + table: "DeviceInstallations", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Products_CategoryId", + table: "Products", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_RoleClaims_RoleId", + table: "RoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "Roles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_TodoItems_UserId", + table: "TodoItems", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_UserClaims_UserId", + table: "UserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_UserLogins_UserId", + table: "UserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_UserRoles_RoleId", + table: "UserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "Users", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_Users_Email", + table: "Users", + column: "Email", + unique: true, + filter: "[Email] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Users_PhoneNumber", + table: "Users", + column: "PhoneNumber", + unique: true, + filter: "[PhoneNumber] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "Users", + column: "NormalizedUserName", + unique: true); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DataProtectionKeys"); + + migrationBuilder.DropTable( + name: "DeviceInstallations"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "RoleClaims"); + + migrationBuilder.DropTable( + name: "TodoItems"); + + migrationBuilder.DropTable( + name: "UserClaims"); + + migrationBuilder.DropTable( + name: "UserLogins"); + + migrationBuilder.DropTable( + name: "UserRoles"); + + migrationBuilder.DropTable( + name: "UserTokens"); + + migrationBuilder.DropTable( + name: "Categories"); + + migrationBuilder.DropTable( + name: "Roles"); + + migrationBuilder.DropTable( + name: "Users"); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/AppDbContextModelSnapshot.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000000..03c510345a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,819 @@ +// <auto-generated /> +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable disable + +namespace Boilerplate.Server.Api.Data.Migrations; + +[DbContext(typeof(AppDbContext))] +partial class AppDbContextModelSnapshot : ModelSnapshot +{ + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.10"); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Categories.Category", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<string>("Color") + .HasColumnType("TEXT"); + + b.Property<byte[]>("ConcurrencyStamp") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property<string>("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b + .HasAnnotation("Cosmos:ContainerName", "Categories") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + + b.HasData( + new + { + Id = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + Color = "#FFCD56", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Ford" + }, + new + { + Id = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + Color = "#FF6384", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Nissan" + }, + new + { + Id = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + Color = "#4BC0C0", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Benz" + }, + new + { + Id = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + Color = "#FF9124", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "BMW" + }, + new + { + Id = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + Color = "#2B88D8", + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Name = "Tesla" + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Identity.Role", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasAnnotation("Cosmos:PropertyName", "_etag"); + + b.Property<string>("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property<string>("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("Roles", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "Roles") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Identity.User", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<int>("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property<long?>("BirthDate") + .HasColumnType("INTEGER"); + + b.Property<string>("ConcurrencyStamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("TEXT") + .HasAnnotation("Cosmos:PropertyName", "_etag"); + + b.Property<string>("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<bool>("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property<long?>("EmailTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("FullName") + .HasColumnType("TEXT"); + + b.Property<int?>("Gender") + .HasColumnType("INTEGER"); + + b.Property<bool>("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property<long?>("LockoutEnd") + .HasColumnType("INTEGER"); + + b.Property<string>("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<string>("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property<long?>("OtpRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("PasswordHash") + .HasColumnType("TEXT"); + + b.Property<string>("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property<bool>("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property<long?>("PhoneNumberTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("ProfileImageName") + .HasColumnType("TEXT"); + + b.Property<long?>("ResetPasswordTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property<string>("Sessions") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<bool>("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property<long?>("TwoFactorTokenRequestedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("Users", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "Users") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + + b.HasData( + new + { + Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), + AccessFailedCount = 0, + BirthDate = 1306790461440000000L, + ConcurrencyStamp = "315e1a26-5b3a-4544-8e91-2760cd28e231", + Email = "test@bitplatform.dev", + EmailConfirmed = true, + EmailTokenRequestedOn = 1306790461440000000L, + FullName = "Boilerplate test account", + Gender = 0, + LockoutEnabled = true, + NormalizedEmail = "TEST@BITPLATFORM.DEV", + NormalizedUserName = "TEST", + PasswordHash = "AQAAAAIAAYagAAAAEP0v3wxkdWtMkHA3Pp5/JfS+42/Qto9G05p2mta6dncSK37hPxEHa3PGE4aqN30Aag==", + PhoneNumber = "+31684207362", + PhoneNumberConfirmed = true, + SecurityStamp = "959ff4a9-4b07-4cc1-8141-c5fc033daf83", + Sessions = "[]", + TwoFactorEnabled = false, + UserName = "test" + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Products.Product", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<Guid>("CategoryId") + .HasColumnType("TEXT"); + + b.Property<byte[]>("ConcurrencyStamp") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property<long>("CreatedOn") + .HasColumnType("INTEGER"); + + b.Property<string>("Description") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property<decimal>("Price") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products"); + + b + .HasAnnotation("Cosmos:ContainerName", "Products") + .HasAnnotation("Cosmos:PartitionKeyName", "CategoryId"); + + b.HasData( + new + { + Id = new Guid("9a59dda2-7b12-4cc1-9658-d2586eef91d4"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "The Ford Mustang is ranked #1 in Sports Cars", + Name = "Mustang", + Price = 27155m + }, + new + { + Id = new Guid("a42914e2-92da-4f0b-aab0-b9572c9671b4"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "The Ford GT is a mid-engine two-seater sports car manufactured and marketed by American automobile manufacturer", + Name = "GT", + Price = 500000m + }, + new + { + Id = new Guid("f75325c8-a213-470b-ab93-4677ca4caeef"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "Ford Ranger is a nameplate that has been used on multiple model lines of pickup trucks sold by Ford worldwide.", + Name = "Ranger", + Price = 25000m + }, + new + { + Id = new Guid("43a82ec1-aab6-445f-83af-a85028417cf7"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306431258624000000L, + Description = "Raptor is a SCORE off-road trophy truck living in a asphalt world", + Name = "Raptor", + Price = 53205m + }, + new + { + Id = new Guid("f01b32bb-eccd-43be-aaf3-3c788a7d7558"), + CategoryId = new Guid("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "The Ford Maverick is a compact pickup truck produced by Ford Motor Company.", + Name = "Maverick", + Price = 22470m + }, + new + { + Id = new Guid("d53bb159-f4f9-493a-b4dc-215fd765ca25"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "A powerful convertible sports car", + Name = "Roadster", + Price = 42800m + }, + new + { + Id = new Guid("74bb268f-18cf-45ec-9f2f-30b34b18fb3c"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "A perfectly adequate family sedan with sharp looks", + Name = "Altima", + Price = 24550m + }, + new + { + Id = new Guid("eb787e1a-7ba8-4708-924b-9f7964fa0f64"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "Legendary supercar with AWD, 4 seats, a powerful V6 engine and the latest tech", + Name = "GT-R", + Price = 113540m + }, + new + { + Id = new Guid("362a6638-0031-485d-825f-e8aeae63a334"), + CategoryId = new Guid("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "A new smart SUV", + Name = "Juke", + Price = 28100m + }, + new + { + Id = new Guid("8629931e-e26e-4885-b561-e447197d4b69"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "", + Name = "H247", + Price = 54950m + }, + new + { + Id = new Guid("a1c1987d-ee6c-41ad-9647-18de4504303a"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "", + Name = "V297", + Price = 103360m + }, + new + { + Id = new Guid("59eea437-bdf2-4c11-b262-06643b253288"), + CategoryId = new Guid("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "", + Name = "R50", + Price = 2000000m + }, + new + { + Id = new Guid("01d223a3-182d-406a-9722-19dab083f96e"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "", + Name = "M550i", + Price = 77790m + }, + new + { + Id = new Guid("64a2616f-3af6-4248-86cf-4a605095a644"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "", + Name = "540i", + Price = 60945m + }, + new + { + Id = new Guid("ac50dc29-4b7e-4d4d-b23a-4227d91f2bb0"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306448953344000000L, + Description = "", + Name = "530e", + Price = 56545m + }, + new + { + Id = new Guid("fb41cc51-9abd-4b45-b0d9-ea8f565ec502"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306440105984000000L, + Description = "", + Name = "530i", + Price = 55195m + }, + new + { + Id = new Guid("e159b1ad-12aa-4e02-a39b-d5e4a32eaf99"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306431258624000000L, + Description = "", + Name = "M850i", + Price = 100045m + }, + new + { + Id = new Guid("4d9cb0f4-1f32-45d5-8c84-d7f15bc569d5"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "", + Name = "X7", + Price = 77980m + }, + new + { + Id = new Guid("1b22319e-0a58-471e-82b6-75cd8b9d98e1"), + CategoryId = new Guid("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306413563904000000L, + Description = "", + Name = "IX", + Price = 87000m + }, + new + { + Id = new Guid("96c73b9c-03df-4f70-ac8d-75c32b89881a"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306466648064000000L, + Description = "rapid acceleration and dynamic handling", + Name = "Model 3", + Price = 61990m + }, + new + { + Id = new Guid("840ba759-bde9-4821-b49b-c981c082bb96"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306457800704000000L, + Description = "finishes near the top of our luxury electric car rankings.", + Name = "Model S", + Price = 135000m + }, + new + { + Id = new Guid("840e113b-5074-4b1c-86bd-e9affb659412"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306448953344000000L, + Description = "Heart-pumping acceleration, long drive range", + Name = "Model X", + Price = 138890m + }, + new + { + Id = new Guid("b2db9074-a0a9-4054-87e2-206b7a55c793"), + CategoryId = new Guid("747f6d66-7524-40ca-8494-f65e85b5ee5d"), + ConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + CreatedOn = 1306422411264000000L, + Description = "extensive driving range, lots of standard safety features", + Name = "Model Y", + Price = 67790m + }); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.PushNotification.DeviceInstallation", b => + { + b.Property<string>("InstallationId") + .HasColumnType("TEXT"); + + b.Property<string>("Auth") + .HasColumnType("TEXT"); + + b.Property<string>("Endpoint") + .HasColumnType("TEXT"); + + b.Property<long>("ExpirationTime") + .HasColumnType("INTEGER"); + + b.Property<string>("P256dh") + .HasColumnType("TEXT"); + + b.Property<string>("Platform") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("PushChannel") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("Tags") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid?>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InstallationId"); + + b.HasIndex("UserId"); + + b.ToTable("DeviceInstallations"); + + b + .HasAnnotation("Cosmos:ContainerName", "DeviceInstallations") + .HasAnnotation("Cosmos:PartitionKeyName", "Platform"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Todo.TodoItem", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property<long>("Date") + .HasColumnType("INTEGER"); + + b.Property<bool>("IsDone") + .HasColumnType("INTEGER"); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("TodoItems"); + + b + .HasAnnotation("Cosmos:ContainerName", "TodoItems") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("FriendlyName") + .HasColumnType("TEXT"); + + b.Property<string>("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + + b + .HasAnnotation("Cosmos:ContainerName", "DataProtectionKeys") + .HasAnnotation("Cosmos:PartitionKeyName", "Id"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ClaimType") + .HasColumnType("TEXT"); + + b.Property<string>("ClaimValue") + .HasColumnType("TEXT"); + + b.Property<Guid>("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "RoleClaims") + .HasAnnotation("Cosmos:PartitionKeyName", "RoleId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ClaimType") + .HasColumnType("TEXT"); + + b.Property<string>("ClaimValue") + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaims", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserClaims") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.Property<string>("LoginProvider") + .HasColumnType("TEXT"); + + b.Property<string>("ProviderKey") + .HasColumnType("TEXT"); + + b.Property<string>("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogins", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserLogins") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<Guid>("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserRoles") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.Property<string>("LoginProvider") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .HasColumnType("TEXT"); + + b.Property<string>("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens", (string)null); + + b + .HasAnnotation("Cosmos:ContainerName", "UserTokens") + .HasAnnotation("Cosmos:PartitionKeyName", "UserId"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Products.Product", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Categories.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.PushNotification.DeviceInstallation", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Todo.TodoItem", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => + { + b.HasOne("Boilerplate.Server.Api.Models.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Boilerplate.Server.Api.Models.Categories.Category", b => + { + b.Navigation("Products"); + }); +#pragma warning restore 612, 618 + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/HttpRequestExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/HttpRequestExtensions.cs index ee4e9cd0d0..3512ff5db3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/HttpRequestExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/HttpRequestExtensions.cs @@ -22,7 +22,7 @@ internal static Uri GetWebClientUrl(this HttpRequest req) { var configuration = req.HttpContext.RequestServices.GetRequiredService<IConfiguration>(); - var webClientUrl = configuration.GetValue<string?>("WebClientUrl"); + var webClientUrl = configuration.Get<ServerApiSettings>()!.WebClientUrl; if (string.IsNullOrEmpty(webClientUrl) is false) return new Uri(webClientUrl); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/SignInManagerExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/SignInManagerExtensions.cs index 3795ead415..8a63f0b23f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/SignInManagerExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Extensions/SignInManagerExtensions.cs @@ -1,27 +1,68 @@ -using System.Reflection; +//+:cnd:noEmit +using System.Reflection; +using Boilerplate.Server.Api; using Boilerplate.Server.Api.Models.Identity; namespace Microsoft.AspNetCore.Identity; public static partial class SignInManagerExtensions { - public static async Task<SignInResult> OtpSignInAsync(this SignInManager<User> signInManager, User user, string otp) + /// <summary> + /// The app invokes <see cref="OtpSignInAsync"/> in the following scenarios: + /// + /// 1. When the user opts to sign in using a 6-digit code received via SMS. + /// 2. When the user chooses to sign in using a 6-digit code sent via email, typically within a magic link. + /// 3. After a successful email confirmation post sign-up, to automatically sign in the confirmed user for an improved user experience. + /// 4. After a successful phone number confirmation post sign-up, to automatically sign in the confirmed user for a smoother user experience. + /// 5. When the browser is redirected to a magic link created after a social sign-in, to automatically authenticate the user. + /// 6. When the user opts to sign in using a 6-digit code delivered via native push notification, web push or SignalR message (if configured). + /// + /// It's important to clarify the authentication method (e.g., Social, SMS, Email, or Push Notification) + /// to avoid sending a second step to the same communication channel. + /// For successful two-step authentication, the user must use a different method for the second step. + /// </summary> + + public static async Task<(SignInResult signInResult, string? authenticationMethod)> OtpSignInAsync(this SignInManager<User> signInManager, User user, string otp) { + var appSettings = signInManager.Context.RequestServices.GetRequiredService<ServerApiSettings>(); + + var expired = (DateTimeOffset.Now - user.OtpRequestedOn) > appSettings.Identity.OtpTokenLifetime; + + if (expired) + throw new BadRequestException(nameof(AppStrings.ExpiredToken)); + var userManager = signInManager.UserManager; - if (await signInManager.CanSignInAsync(user) is false) return SignInResult.NotAllowed; + if (await signInManager.CanSignInAsync(user) is false) return (SignInResult.NotAllowed, null); - if (await userManager.IsLockedOutAsync(user)) return SignInResult.LockedOut; + if (await userManager.IsLockedOutAsync(user)) return (SignInResult.LockedOut, null); - bool tokenIsValid = await userManager.VerifyUserTokenAsync(user!, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp,{user.OtpRequestedOn?.ToUniversalTime()}"), otp!); + bool tokenIsValid = false; + string? authenticationMethod = null; + string[] authenticationMethods = ["Email", + "Sms", + //#if (notification == true || signalR == true) + "Push", // => Native push notification, web push or SignalR message. + //#endif + "Social"]; + + foreach (var authMethod in authenticationMethods) + { + tokenIsValid = await userManager.VerifyUserTokenAsync(user!, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_{authMethod},{user.OtpRequestedOn?.ToUniversalTime()}"), otp!); + if (tokenIsValid) + { + authenticationMethod = authMethod; + break; + } + } if (tokenIsValid is false) { await userManager.AccessFailedAsync(user); - return SignInResult.Failed; + return (SignInResult.Failed, null); } - return await SignInOrTwoFactorAsync(signInManager, user); // See SignInManager.SignInOrTwoFactorAsync in aspnetcore repo + return (await SignInOrTwoFactorAsync(signInManager, user), authenticationMethod); // See SignInManager.SignInOrTwoFactorAsync in aspnetcore repo } private static async Task<SignInResult> SignInOrTwoFactorAsync(SignInManager<User> signInManager, User user) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Hubs/AppHub.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Hubs/AppHub.cs deleted file mode 100644 index d9ec736bb1..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Hubs/AppHub.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.SignalR; - -namespace Boilerplate.Server.Api.Hubs; - -[AllowAnonymous] -public partial class AppHub : Hub -{ -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/CategoriesMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/CategoriesMapper.cs index e0591f8826..22451a258c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/CategoriesMapper.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/CategoriesMapper.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Server.Api.Mappers; /// <summary> /// More info at Server/Mappers/README.md /// </summary> -[Mapper(UseDeepCloning = true)] +[Mapper] public static partial class CategoriesMapper { public static partial IQueryable<CategoryDto> Project(this IQueryable<Category> query); @@ -20,6 +20,4 @@ public static partial class CategoriesMapper [MapProperty(nameof(@Category.Products.Count), nameof(@CategoryDto.ProductsCount))] public static partial CategoryDto Map(this Category source); public static partial Category Map(this CategoryDto source); - public static partial void Patch(this CategoryDto source, Category destination); - public static partial void Patch(this Category source, CategoryDto destination); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs index 714a58e688..f0be27a736 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Server.Api.Mappers; /// <summary> /// More info at Server/Mappers/README.md /// </summary> -[Mapper(UseDeepCloning = true)] +[Mapper] public static partial class IdentityMapper { public static partial UserDto Map(this User source); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/ProductsMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/ProductsMapper.cs index 4826dce036..a12cd69244 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/ProductsMapper.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/ProductsMapper.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Server.Api.Mappers; /// <summary> /// More info at Server/Mappers/README.md /// </summary> -[Mapper(UseDeepCloning = true)] +[Mapper] public static partial class ProductsMapper { public static partial IQueryable<ProductDto> Project(this IQueryable<Product> query); @@ -20,13 +20,4 @@ public static partial class ProductsMapper [MapProperty(nameof(@Product.Category.Name), nameof(@ProductDto.CategoryName))] public static partial ProductDto Map(this Product source); public static partial Product Map(this ProductDto source); - public static partial void Patch(this ProductDto source, Product destination); - - // It is important to note that when a ProductDto is sent to the server, - // it may contain a 'CategoryName'. However, while Mappingly's automatic conventions may fill Category property of product model - // with only its Name property populated by the 'CategoryName' value while the Id remains 0, - // this oversight could lead to ef core mistakenly assuming a new category is being added to the database during product saving, - // resulting in unintended data persistence. That's why we need to ignore 'CategoryName' during Patching and Mapping manually. - [MapperIgnoreSource(nameof(Product.Category))] - public static partial void Patch(this Product source, ProductDto destination); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/PushNotificationMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/PushNotificationMapper.cs new file mode 100644 index 0000000000..34789b0034 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/PushNotificationMapper.cs @@ -0,0 +1,14 @@ +using Boilerplate.Server.Api.Models.PushNotification; +using Boilerplate.Shared.Dtos.PushNotification; +using Riok.Mapperly.Abstractions; + +namespace Boilerplate.Server.Api.Mappers; + +/// <summary> +/// More info at Server/Mappers/README.md +/// </summary> +[Mapper] +public static partial class PushNotificationMapper +{ + public static partial void Patch(this DeviceInstallationDto source, DeviceInstallation destination); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs index a677f50528..55687a8c49 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Server.Api.Mappers; /// <summary> /// More info at Server/Mappers/README.md /// </summary> -[Mapper(UseDeepCloning = true)] +[Mapper] public static partial class TodoMapper { public static partial IQueryable<TodoItemDto> Project(this IQueryable<TodoItem> query); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs index d41575eb7f..499f296964 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs @@ -11,5 +11,7 @@ public partial class Category public string? Color { get; set; } + public byte[] ConcurrencyStamp { get; set; } = []; + public IList<Product> Products { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs index ed66a9e53d..ba8e23715d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs @@ -21,4 +21,6 @@ public partial class Product public Category? Category { get; set; } public Guid CategoryId { get; set; } + + public byte[] ConcurrencyStamp { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/PushNotification/DeviceInstallation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/PushNotification/DeviceInstallation.cs new file mode 100644 index 0000000000..cf89c233e9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/PushNotification/DeviceInstallation.cs @@ -0,0 +1,31 @@ +using Boilerplate.Server.Api.Models.Identity; + +namespace Boilerplate.Server.Api.Models.PushNotification; + +public class DeviceInstallation +{ + [Required, Key] + public string? InstallationId { get; set; } + + [Required, AllowedValues("apns", "fcmV1", "browser")] + public string? Platform { get; set; } + + [Required] + public string? PushChannel { get; set; } + + public string? P256dh { get; set; } + public string? Auth { get; set; } + public string? Endpoint { get; set; } + + public Guid? UserId { get; set; } + + [ForeignKey(nameof(UserId))] + public User? User { get; set; } + + public string[] Tags { get; set; } = []; + + /// <summary> + /// Unix Time Seconds + /// </summary> + public long ExpirationTime { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs index 1e6f4492c6..6b437d9a04 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs @@ -9,12 +9,19 @@ public static partial class Program /// <summary> /// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0#middleware-order /// </summary> - public static void ConfiureMiddlewares(this WebApplication app) + private static void ConfigureMiddlewares(this WebApplication app) { var configuration = app.Configuration; var env = app.Environment; - app.UseForwardedHeaders(); + var forwardedHeadersOptions = configuration.Get<ServerApiSettings>()!.ForwardedHeaders; + + if (forwardedHeadersOptions is not null + && (app.Environment.IsDevelopment() || forwardedHeadersOptions.AllowedHosts.Any())) + { + // If the list is empty then all hosts are allowed. Failing to restrict this these values may allow an attacker to spoof links generated for reset password etc. + app.UseForwardedHeaders(forwardedHeadersOptions); + } if (CultureInfoManager.MultilingualEnabled) { @@ -32,7 +39,7 @@ public static void ConfiureMiddlewares(this WebApplication app) app.UseExceptionHandler("/", createScopeForErrors: true); - if(env.IsDevelopment() is false) + if (env.IsDevelopment() is false) { app.UseHttpsRedirection(); app.UseResponseCompression(); @@ -69,8 +76,8 @@ public static void ConfiureMiddlewares(this WebApplication app) QueryStringParameter = queryStringParameter }).WithTags("Test"); - //#if (signalr == true) - app.MapHub<Hubs.AppHub>("/app-hub"); + //#if (signalR == true) + app.MapHub<SignalR.AppHub>("/app-hub"); //#endif app.MapControllers().RequireAuthorization(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs index f63322cdb3..72b4b9ed1e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs @@ -1,41 +1,95 @@ //+:cnd:noEmit -using System.IO.Compression; -using Microsoft.AspNetCore.ResponseCompression; -using Boilerplate.Server.Api.Services; using System.Net; using System.Net.Mail; +using System.IO.Compression; using System.Text.RegularExpressions; using System.Security.Cryptography.X509Certificates; -using Boilerplate.Server.Api.Models.Identity; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.OData; using Microsoft.Net.Http.Headers; using Microsoft.IdentityModel.Tokens; -using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.ResponseCompression; +using Twilio; +using PhoneNumbers; using FluentStorage; using FluentStorage.Blobs; -using Twilio; +using FluentEmail.Core; +//#if (notification == true) +using AdsPush; +using AdsPush.Abstraction; +//#endif +using Boilerplate.Server.Api.Services; using Boilerplate.Server.Api.Controllers; +using Boilerplate.Server.Api.Models.Identity; +using Boilerplate.Server.Api.Services.Identity; namespace Boilerplate.Server.Api; public static partial class Program { - public static void ConfigureApiServices(this WebApplicationBuilder builder) + public static void AddServerApiProjectServices(this WebApplicationBuilder builder) { // Services being registered here can get injected in server project only. - + var env = builder.Environment; var services = builder.Services; var configuration = builder.Configuration; - var env = builder.Environment; + var appSettings = configuration.Get<ServerApiSettings>()!; - services.AddExceptionHandler<ServerExceptionHandler>(); + services.AddScoped<EmailService>(); + services.AddScoped<PhoneService>(); + if (appSettings.Sms?.Configured is true) + { + TwilioClient.Init(appSettings.Sms.TwilioAccountSid, appSettings.Sms.TwilioAutoToken); + } - services.AddOptions<ForwardedHeadersOptions>() - .Bind(configuration.GetRequiredSection("ForwardedHeaders")) - .ValidateDataAnnotations() - .ValidateOnStart(); + services.AddSingleton(_ => PhoneNumberUtil.GetInstance()); + services.AddSingleton<IBlobStorage>(sp => + { + //#if (filesStorage == "Local") + var isRunningInsideDocker = Directory.Exists("/container_volume"); // It's supposed to be a mounted volume named /container_volume + var attachmentsDirPath = Path.Combine(isRunningInsideDocker ? "/container_volume" : Directory.GetCurrentDirectory(), "App_Data"); + Directory.CreateDirectory(attachmentsDirPath); + return StorageFactory.Blobs.DirectoryFiles(attachmentsDirPath); + //#elif (filesStorage == "AzureBlobStorage") + var azureBlobStorageSasUrl = configuration.GetConnectionString("AzureBlobStorageSasUrl"); + return (IBlobStorage)(azureBlobStorageSasUrl is "emulator" + ? StorageFactory.Blobs.AzureBlobStorageWithLocalEmulator() + : StorageFactory.Blobs.AzureBlobStorageWithSas(azureBlobStorageSasUrl)); + //#else + // Note that FluentStorage.AWS can be used with any S3 compatible S3 implementation such as Digital Ocean's Spaces Object Storage. + throw new NotImplementedException("Install and configure any storage supported by fluent storage (https://github.com/robinrodricks/FluentStorage/wiki/Blob-Storage)"); + //#endif + }); + //#if (notification == true) + services.AddSingleton(_ => + { + var adsPushSenderBuilder = new AdsPushSenderBuilder(); + + if (string.IsNullOrEmpty(appSettings.AdsPushAPNS?.P8PrivateKey) is false) + { + adsPushSenderBuilder = adsPushSenderBuilder.ConfigureApns(appSettings.AdsPushAPNS, null); + } + + if (string.IsNullOrEmpty(appSettings.AdsPushFirebase?.PrivateKey) is false) + { + appSettings.AdsPushFirebase.PrivateKey = appSettings.AdsPushFirebase.PrivateKey.Replace(@"\n", string.Empty); + + adsPushSenderBuilder = adsPushSenderBuilder.ConfigureFirebase(appSettings.AdsPushFirebase, AdsPushTarget.Android); + } + + if (string.IsNullOrEmpty(appSettings.AdsPushVapid?.PrivateKey) is false) + { + adsPushSenderBuilder = adsPushSenderBuilder.ConfigureVapid(appSettings.AdsPushVapid, null); + } + + return adsPushSenderBuilder + .BuildSender(); + }); + services.AddScoped<PushNotificationService>(); + //#endif + + services.AddExceptionHandler<ServerExceptionHandler>(); services.AddResponseCaching(); @@ -55,8 +109,6 @@ public static void ConfigureApiServices(this WebApplicationBuilder builder) services.AddApplicationInsightsTelemetry(configuration); //#endif - var appSettings = configuration.GetSection(nameof(AppSettings)).Get<AppSettings>()!; - services.AddCors(builder => { builder.AddDefaultPolicy(policy => @@ -66,10 +118,10 @@ public static void ConfigureApiServices(this WebApplicationBuilder builder) policy.SetPreflightMaxAge(TimeSpan.FromDays(1)); // https://stackoverflow.com/a/74184331 } - var webClientUrl = configuration.GetValue<string?>("WebClientUrl"); + var webClientUrl = configuration.Get<ServerApiSettings>()!.WebClientUrl; policy.SetIsOriginAllowed(origin => - LocalhostOriginRegex().IsMatch(origin) || + AllowedOriginsRegex().IsMatch(origin) || (string.IsNullOrEmpty(webClientUrl) is false && string.Equals(origin, webClientUrl, StringComparison.InvariantCultureIgnoreCase))) .AllowAnyHeader() .AllowAnyMethod() @@ -79,11 +131,11 @@ public static void ConfigureApiServices(this WebApplicationBuilder builder) services.AddAntiforgery(); - services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.Add(AppJsonContext.Default)); + services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.AddRange([AppJsonContext.Default, IdentityJsonContext.Default])); services .AddControllers() - .AddJsonOptions(options => options.JsonSerializerOptions.TypeInfoResolverChain.Add(AppJsonContext.Default)) + .AddJsonOptions(options => options.JsonSerializerOptions.TypeInfoResolverChain.AddRange([AppJsonContext.Default, IdentityJsonContext.Default])) //#if (api == "Integrated") .AddApplicationPart(typeof(AppControllerBase).Assembly) //#endif @@ -97,8 +149,11 @@ public static void ConfigureApiServices(this WebApplicationBuilder builder) }; }); - //#if (signalr == true) - services.AddSignalR(); + //#if (signalR == true) + services.AddSignalR(options => + { + options.EnableDetailedErrors = env.IsDevelopment(); + }); //#endif services.AddPooledDbContextFactory<AppDbContext>(AddDbContext); @@ -143,17 +198,17 @@ void AddDbContext(DbContextOptionsBuilder options) //#endif }; - services.AddOptions<AppSettings>() - .Bind(configuration.GetRequiredSection(nameof(AppSettings))) + services.AddOptions<IdentityOptions>() + .Bind(configuration.GetRequiredSection(nameof(ServerApiSettings.Identity))) .ValidateDataAnnotations() .ValidateOnStart(); - services.AddOptions<IdentityOptions>() - .Bind(configuration.GetRequiredSection(nameof(AppSettings)).GetRequiredSection(nameof(AppSettings.Identity))) + services.AddOptions<ServerApiSettings>() + .Bind(configuration) .ValidateDataAnnotations() .ValidateOnStart(); - services.TryAddTransient(sp => sp.GetRequiredService<IOptionsSnapshot<AppSettings>>().Value); + services.AddSingleton(sp => configuration.Get<ServerApiSettings>()!); services.AddEndpointsApiExplorer(); @@ -161,9 +216,10 @@ void AddDbContext(DbContextOptionsBuilder options) AddIdentity(builder); - var fluentEmailServiceBuilder = services.AddFluentEmail(appSettings.Email.DefaultFromEmail); + var emailSettings = appSettings.Email ?? throw new InvalidOperationException("Email settings are required."); + var fluentEmailServiceBuilder = services.AddFluentEmail(emailSettings.DefaultFromEmail); - if (appSettings.Email.UseLocalFolderForEmails) + if (emailSettings.UseLocalFolderForEmails) { var isRunningInsideDocker = Directory.Exists("/container_volume"); // It's supposed to be a mounted volume named /container_volume var sentEmailsFolderPath = Path.Combine(isRunningInsideDocker ? "/container_volume" : Directory.GetCurrentDirectory(), "App_Data", "sent-emails"); @@ -178,51 +234,20 @@ void AddDbContext(DbContextOptionsBuilder options) } else { - if (appSettings.Email.HasCredential) + if (emailSettings.HasCredential) { - fluentEmailServiceBuilder.AddSmtpSender(() => new(appSettings.Email.Host, appSettings.Email.Port) + fluentEmailServiceBuilder.AddSmtpSender(() => new(emailSettings.Host, emailSettings.Port) { - Credentials = new NetworkCredential(appSettings.Email.UserName, appSettings.Email.Password), + Credentials = new NetworkCredential(emailSettings.UserName, emailSettings.Password), EnableSsl = true }); } else { - fluentEmailServiceBuilder.AddSmtpSender(appSettings.Email.Host, appSettings.Email.Port); + fluentEmailServiceBuilder.AddSmtpSender(emailSettings.Host, emailSettings.Port); } } - services.TryAddTransient<EmailService>(); - services.TryAddTransient<SmsService>(); - if (appSettings.Sms.Configured) - { - TwilioClient.Init(appSettings.Sms.TwilioAccountSid, appSettings.Sms.TwilioAutoToken); - } - - //#if (filesStorage == "Local") - services.TryAddSingleton(sp => - { - var isRunningInsideDocker = Directory.Exists("/container_volume"); // It's supposed to be a mounted volume named /container_volume - var attachmentsDirPath = Path.Combine(isRunningInsideDocker ? "/container_volume" : Directory.GetCurrentDirectory(), "App_Data"); - Directory.CreateDirectory(attachmentsDirPath); - return StorageFactory.Blobs.DirectoryFiles(attachmentsDirPath); - }); - //#elif (filesStorage == "AzureBlobStorage") - services.TryAddSingleton(sp => - { - var azureBlobStorageSasUrl = configuration.GetConnectionString("AzureBlobStorageSasUrl"); - return (IBlobStorage)(azureBlobStorageSasUrl is "emulator" - ? StorageFactory.Blobs.AzureBlobStorageWithLocalEmulator() - : StorageFactory.Blobs.AzureBlobStorageWithSas(azureBlobStorageSasUrl)); - }); - //#else - services.TryAddSingleton<IBlobStorage>(sp => - { - // Note that FluentStorage.AWS can be used with any S3 compatible S3 implementation such as Digital Ocean's Spaces Object Storage. - throw new NotImplementedException("Install and configure any storage supported by fluent storage (https://github.com/robinrodricks/FluentStorage/wiki/Blob-Storage)"); - }); - //#endif - //#if (captcha == "reCaptcha") services.AddHttpClient<GoogleRecaptchaHttpClient>(c => { @@ -236,23 +261,16 @@ private static void AddIdentity(WebApplicationBuilder builder) var services = builder.Services; var configuration = builder.Configuration; var env = builder.Environment; - var appSettings = configuration.GetSection(nameof(AppSettings)).Get<AppSettings>()!; + var appSettings = configuration.Get<ServerApiSettings>()!; var identityOptions = appSettings.Identity; var certificatePath = Path.Combine(AppContext.BaseDirectory, "DataProtectionCertificate.pfx"); - var certificate = new X509Certificate2(certificatePath, configuration.GetRequiredValue<string>("DataProtectionCertificatePassword"), OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet); - - bool isTestCertificate = certificate.Thumbprint is "55140A8C935AB5202949071E5781E6946CD60606"; // The default test certificate is still in use - if (isTestCertificate && env.IsDevelopment() is false) - { - throw new InvalidOperationException(@"The default test certificate is still in use. Please replace it with a new one by running the 'dotnet dev-certs https --export-path DataProtectionCertificate.pfx --password P@ssw0rdP@ssw0rd' command (or your preferred method for generating PFX files) in the server project's folder."); - } + var certificate = new X509Certificate2(certificatePath, appSettings.DataProtectionCertificatePassword, OperatingSystem.IsWindows() ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet); services.AddDataProtection() .PersistKeysToDbContext<AppDbContext>() .ProtectKeysWithCertificate(certificate); - services.AddTransient<IUserConfirmation<User>, AppUserConfirmation>(); services.AddIdentity<User, Role>() .AddEntityFrameworkStores<AppDbContext>() @@ -261,6 +279,11 @@ private static void AddIdentity(WebApplicationBuilder builder) .AddClaimsPrincipalFactory<AppUserClaimsPrincipalFactory>() .AddApiEndpoints(); + services.AddScoped<IUserConfirmation<User>, AppUserConfirmation>(); + services.AddScoped(sp => (IUserEmailStore<User>)sp.GetRequiredService<IUserStore<User>>()); + services.AddScoped(sp => (IUserPhoneNumberStore<User>)sp.GetRequiredService<IUserStore<User>>()); + services.AddScoped(sp => (AppUserClaimsPrincipalFactory)sp.GetRequiredService<IUserClaimsPrincipalFactory<User>>()); + var authenticationBuilder = services.AddAuthentication(options => { options.DefaultAuthenticateScheme = IdentityConstants.BearerScheme; @@ -379,8 +402,8 @@ private static void AddSwaggerGen(WebApplicationBuilder builder) } /// <summary> - /// For either Blazor Hybrid web view or localhost in dev environment. + /// For either Blazor Hybrid web view, localhost, dev tunnels etc in dev environment. /// </summary> - [GeneratedRegex(@"^(http|https|app):\/\/(localhost|0\.0\.0\.0|127\.0\.0\.1)(:\d+)?(\/.*)?$")] - private static partial Regex LocalhostOriginRegex(); + [GeneratedRegex(@"^(http|https|app):\/\/(localhost|0\.0\.0\.0|0\.0\.0\.1|127\.0\.0\.1|.*?devtunnels\.ms|.*?github\.dev)(:\d+)?(\/.*)?$")] + private static partial Regex AllowedOriginsRegex(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.cs index 96ec3b2d0d..298f437122 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.cs @@ -12,24 +12,24 @@ public static async Task Main(string[] args) builder.Configuration.AddSharedConfigurations(); // The following line (using the * in the URL), allows the emulators and mobile devices to access the app using the host IP address. - if (AppEnvironment.IsDev() && OperatingSystem.IsWindows()) + if (builder.Environment.IsDevelopment() && OperatingSystem.IsWindows()) { builder.WebHost.UseUrls("http://localhost:5031", "http://*:5031"); } - builder.ConfigureApiServices(); - builder.Services.AddSharedProjectServices(); + builder.Services.AddSharedProjectServices(builder.Configuration); + builder.AddServerApiProjectServices(); var app = builder.Build(); - if (AppEnvironment.IsDev()) + if (builder.Environment.IsDevelopment()) { await using var scope = app.Services.CreateAsyncScope(); var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); await dbContext.Database.EnsureCreatedAsync(); } - app.ConfiureMiddlewares(); + app.ConfigureMiddlewares(); await app.RunAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.fr.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.fr.resx deleted file mode 100644 index b187728c63..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.fr.resx +++ /dev/null @@ -1,193 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +:cnd:noEmit --> -<root> - <!-- - Microsoft ResX Schema - - Version 2.0 - - The primary goals of this format is to allow a simple XML format - that is mostly human readable. The generation and parsing of the - various data types are done through the TypeConverter classes - associated with the data types. - - Example: - - ... ado.net/XML headers & schema ... - <resheader name="resmimetype">text/microsoft-resx</resheader> - <resheader name="version">2.0</resheader> - <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> - <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> - <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> - <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> - <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> - <value>[base64 mime encoded serialized .NET Framework object]</value> - </data> - <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> - <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> - <comment>This is a comment</comment> - </data> - - There are any number of "resheader" rows that contain simple - name/value pairs. - - Each data row contains a name, and value. The row also contains a - type or mimetype. Type corresponds to a .NET class that support - text/value conversion through the TypeConverter architecture. - Classes that don't support this are serialized and stored with the - mimetype set. - - The mimetype is used for serialized objects, and tells the - ResXResourceReader how to depersist the object. This is currently not - extensible. For a given mimetype the value must be set accordingly: - - Note - application/x-microsoft.net.object.binary.base64 is the format - that the ResXResourceWriter will generate, however the reader can - read any of the formats listed below. - - mimetype: application/x-microsoft.net.object.binary.base64 - value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter - : and then encoded with base64 encoding. - - mimetype: application/x-microsoft.net.object.soap.base64 - value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Soap.SoapFormatter - : and then encoded with base64 encoding. - - mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array - : using a System.ComponentModel.TypeConverter - : and then encoded with base64 encoding. - --> - <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> - <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> - <xsd:element name="root" msdata:IsDataSet="true"> - <xsd:complexType> - <xsd:choice maxOccurs="unbounded"> - <xsd:element name="metadata"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" /> - </xsd:sequence> - <xsd:attribute name="name" use="required" type="xsd:string" /> - <xsd:attribute name="type" type="xsd:string" /> - <xsd:attribute name="mimetype" type="xsd:string" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="assembly"> - <xsd:complexType> - <xsd:attribute name="alias" type="xsd:string" /> - <xsd:attribute name="name" type="xsd:string" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="data"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> - <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> - <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="resheader"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" /> - </xsd:complexType> - </xsd:element> - </xsd:choice> - </xsd:complexType> - </xsd:element> - </xsd:schema> - <resheader name="resmimetype"> - <value>text/microsoft-resx</value> - </resheader> - <resheader name="version"> - <value>2.0</value> - </resheader> - <resheader name="reader"> - <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <resheader name="writer"> - <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <data name="ConfirmationEmailSubject" xml:space="preserve"> - <value>Boilerplate {0} - Confirmez votre adresse e-mail</value> - </data> - <data name="EmailConfirmationMessageSubtitle" xml:space="preserve"> - <value>Vous recevez ce e-mail car vous avez récemment enregistré {0} sur votre compte Boilerplate.</value> - </data> - <data name="EmailConfirmationMessageBodyToken" xml:space="preserve"> - <value>Confirmez votre adresse e-mail en saisissant le numéro ci-dessous dans l'application.</value> - </data> - <data name="EmailConfirmationMessageBodyLink" xml:space="preserve"> - <value>Ou cliquez sur le lien suivant pour confirmer cette adresse email.</value> - </data> - <data name="ResetPasswordEmailSubject" xml:space="preserve"> - <value>Boilerplate {0} - Réinitialisez votre mot de passe</value> - </data> - <data name="ResetPasswordTitle" xml:space="preserve"> - <value>Bonjour {0}</value> - </data> - <data name="ResetPasswordSubtitle" xml:space="preserve"> - <value>Quelqu'un a demandé un jeton pour changer votre mot de passe.</value> - </data> - <data name="ResetPasswordBody" xml:space="preserve"> - <value>Si ce n'est pas vous qui avez demandé la réinitialisation du mot de passe, vous pouvez ignorer cet e-mail.</value> - </data> - <data name="ResetPasswordTokenMessage" xml:space="preserve"> - <value>Vous pouvez copier le jeton ci-dessous et le saisir sur la page de réinitialisation du mot de passe.</value> - </data> - <data name="ResetPasswordLinkMessage" xml:space="preserve"> - <value>Ou vous pouvez cliquer sur le lien suivant pour essayer de réinitialiser votre mot de passe.</value> - </data> - <data name="ResetYourPassword" xml:space="preserve"> - <value>réinitialisez votre mot de passe</value> - </data> - <data name="OtpEmailSubject" xml:space="preserve"> - <value>Boilerplate {0} - OTP</value> - </data> - <data name="OtpTitle" xml:space="preserve"> - <value>Bonjour {0}</value> - </data> - <data name="OtpSubtitle" xml:space="preserve"> - <value>Quelqu'un a demandé un OTP pour se connecter à l'application.</value> - </data> - <data name="OtpBody" xml:space="preserve"> - <value>Si vous n'avez pas demandé l'OTP, vous pouvez ignorer cet e-mail.</value> - </data> - <data name="OtpMessage" xml:space="preserve"> - <value>You can copy the below OTP and enter it in the login page.</value> - </data> - <data name="OtpLinkMessage" xml:space="preserve"> - <value>Ou vous pouvez cliquer sur le lien suivant pour vous connecter.</value> - </data> - <data name="AppName" xml:space="preserve"> - <value>Panneau d'administration</value> - </data> - <data name="WelcomeToApp" xml:space="preserve"> - <value>Bienvenue dans le panneau d'administration !</value> - </data> - <data name="TfaTokenEmailSubject" xml:space="preserve"> - <value>Boilerplate {0} - 2FA token</value> - </data> - <data name="TfaTokenHello" xml:space="preserve"> - <value>Bonjour chère {0}</value> - </data> - <data name="TfaTokenMessage" xml:space="preserve"> - <value>Voici votre nouveau token 2FA pour vous connecter:</value> - </data> - <data name="CopyTokenNote" xml:space="preserve"> - <value>Copiez ce code et utilisez-le dans la page de connexion.</value> - </data> - <data name="DefaultFromName" xml:space="preserve"> - <value>Boilerplate app</value> - </data> -</root> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.nl.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.nl.resx new file mode 100644 index 0000000000..b494678387 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Resources/EmailStrings.nl.resx @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +:cnd:noEmit --> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ConfirmationEmailSubject" xml:space="preserve"> + <value>Boilerplate {0} - bevestig uw e -mailadres</value> + </data> + <data name="EmailConfirmationMessageSubtitle" xml:space="preserve"> + <value>Je ontvangt deze e-mail omdat je onlangs {0} hebt geregistreerd op je Boilerplate-account.</value> + </data> + <data name="EmailConfirmationMessageBodyToken" xml:space="preserve"> + <value>Bevestig je e-mailadres door onderstaand nummer in te vullen in de app.</value> + </data> + <data name="EmailConfirmationMessageBodyLink" xml:space="preserve"> + <value>Of klik op de volgende link om dit e-mailadres te bevestigen.</value> + </data> + <data name="ResetPasswordEmailSubject" xml:space="preserve"> + <value>Boilerplate {0} - Stel uw wachtwoord opnieuw in</value> + </data> + <data name="ResetPasswordTitle" xml:space="preserve"> + <value>Hallo {0}</value> + </data> + <data name="ResetPasswordSubtitle" xml:space="preserve"> + <value>Iemand heeft een token aangevraagd om je wachtwoord te wijzigen.</value> + </data> + <data name="ResetPasswordBody" xml:space="preserve"> + <value>Als u niet degene bent die om het wachtwoord opnieuw in te stellen heeft gevraagd, kunt u deze e-mail negeren.</value> + </data> + <data name="ResetPasswordTokenMessage" xml:space="preserve"> + <value>U kunt het onderstaande token kopiëren en invoeren op de pagina voor het opnieuw instellen van het wachtwoord.</value> + </data> + <data name="ResetPasswordLinkMessage" xml:space="preserve"> + <value>Of u kunt op de volgende link klikken om te proberen uw wachtwoord opnieuw in te stellen.</value> + </data> + <data name="ResetYourPassword" xml:space="preserve"> + <value>Je wachtwoord opnieuw instellen</value> + </data> + <data name="OtpEmailSubject" xml:space="preserve"> + <value>Boilerplate {0} - OTP</value> + </data> + <data name="OtpTitle" xml:space="preserve"> + <value>Hallo {0}</value> + </data> + <data name="OtpSubtitle" xml:space="preserve"> + <value>Iemand heeft een OTP aangevraagd om in te loggen op de app.</value> + </data> + <data name="OtpBody" xml:space="preserve"> + <value>Als je de OTP niet hebt aangevraagd, kun je deze e-mail negeren.</value> + </data> + <data name="OtpMessage" xml:space="preserve"> + <value>U kunt de onderstaande OTP kopiëren en invoeren op de inlogpagina.</value> + </data> + <data name="OtpLinkMessage" xml:space="preserve"> + <value>Of u kunt op de volgende link klikken om in te loggen.</value> + </data> + <data name="AppName" xml:space="preserve"> + <value>Standaardtekst</value> + </data> + <data name="WelcomeToApp" xml:space="preserve"> + <value>Welkom bij Boilerplate!</value> + </data> + <data name="TfaTokenEmailSubject" xml:space="preserve"> + <value>Boilerplate {0} - 2FA-token</value> + </data> + <data name="TfaTokenHello" xml:space="preserve"> + <value>Hallo lieve {0}</value> + </data> + <data name="TfaTokenMessage" xml:space="preserve"> + <value>Hier is uw nieuwe 2FA-token om in te loggen:</value> + </data> + <data name="CopyTokenNote" xml:space="preserve"> + <value>Kopieer deze code en gebruik deze in de inlogpagina.</value> + </data> + <data name="DefaultFromName" xml:space="preserve"> + <value>Boilerplate-app</value> + </data> +</root> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs new file mode 100644 index 0000000000..cc93f6cd28 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs @@ -0,0 +1,156 @@ +//+:cnd:noEmit +//#if (notification == true) +using AdsPush.Abstraction.Settings; +//#endif + +namespace Boilerplate.Server.Api; + +public partial class ServerApiSettings : SharedSettings +{ + /// <summary> + /// It can also be configured using: dotnet user-secrets set 'DataProtectionCertificatePassword' '@nyPassw0rd' + /// </summary> + [Required] + public string DataProtectionCertificatePassword { get; set; } = default!; + + [Required] + public AppIdentityOptions Identity { get; set; } = default!; + + [Required] + public EmailOptions Email { get; set; } = default!; + + public SmsOptions? Sms { get; set; } + + [Required] + public string UserProfileImagesDir { get; set; } = default!; + + //#if (captcha == "reCaptcha") + [Required] + public string GoogleRecaptchaSecretKey { get; set; } = default!; + //#endif + + //#if (notification == true) + public AdsPushVapidSettings? AdsPushVapid { get; set; } + + public AdsPushFirebaseSettings? AdsPushFirebase { get; set; } + + public AdsPushAPNSSettings? AdsPushAPNS { get; set; } + //#endif + + public ForwardedHeadersOptions? ForwardedHeaders { get; set; } + + public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = base.Validate(validationContext).ToList(); + + if (Identity is null) + throw new InvalidOperationException("Identity configuration is required."); + if (Email is null) + throw new InvalidOperationException("Email configuration is required."); + + Validator.TryValidateObject(Identity, new ValidationContext(Identity), validationResults, true); + Validator.TryValidateObject(Email, new ValidationContext(Email), validationResults, true); + if (Sms is not null) + { + Validator.TryValidateObject(Sms, new ValidationContext(Sms), validationResults, true); + } + //#if (notification == true) + if (AdsPushVapid is not null) + { + Validator.TryValidateObject(AdsPushVapid, new ValidationContext(AdsPushVapid), validationResults, true); + } + //#endif + if (ForwardedHeaders is not null) + { + Validator.TryValidateObject(ForwardedHeaders, new ValidationContext(ForwardedHeaders), validationResults, true); + } + + if (AppEnvironment.IsDev() is false) + { + if (DataProtectionCertificatePassword is "P@ssw0rdP@ssw0rd") + { + throw new InvalidOperationException(@"The default test certificate is still in use. Please replace it with a new one by running the 'dotnet dev-certs https --export-path DataProtectionCertificate.pfx --password @nyPassw0rd' +command in the Server.Api's project's folder and replace P@ssw0rdP@ssw0rd with the new password."); + } + + //#if (captcha == "reCaptcha") + if (GoogleRecaptchaSecretKey is "6LdMKr4pAAAAANvngWNam_nlHzEDJ2t6SfV6L_DS") + { + throw new InvalidOperationException("The GoogleRecaptchaSecretKey is not set. Please set it in the server's appsettings.json file."); + } + //#endif + + //#if (notification == true) + if (AdsPushVapid?.PrivateKey is "dMIR1ICj-lDWYZ-ZYCwXKyC2ShYayYYkEL-oOPnpq9c" || AdsPushVapid?.Subject is "mailto: <test@bitplatform.dev>") + { + throw new InvalidOperationException("The AdsPushVapid's PrivateKey and Subject are not set. Please set them in the server's appsettings.json file."); + } + //#endif + } + + return validationResults; + } +} + +public partial class AppIdentityOptions : IdentityOptions +{ + /// <summary> + /// BearerTokenExpiration used as JWT's expiration claim, access token's expires in and cookie's max age. + /// </summary> + public TimeSpan BearerTokenExpiration { get; set; } + public TimeSpan RefreshTokenExpiration { get; set; } + + [Required] + public string Issuer { get; set; } = default!; + + [Required] + public string Audience { get; set; } = default!; + + /// <summary> + /// To either confirm and/or change email + /// </summary> + public TimeSpan EmailTokenLifetime { get; set; } + /// <summary> + /// To either confirm and/or change phone number + /// </summary> + public TimeSpan PhoneNumberTokenLifetime { get; set; } + public TimeSpan ResetPasswordTokenLifetime { get; set; } + public TimeSpan TwoFactorTokenLifetime { get; set; } + + /// <summary> + /// To sign in with either Otp or magic link. + /// </summary> + public TimeSpan OtpTokenLifetime { get; set; } + + public TimeSpan RevokeUserSessionsDelay { get; set; } +} + +public partial class EmailOptions +{ + [Required] + public string Host { get; set; } = default!; + /// <summary> + /// If true, the web app tries to store emails as .eml file in the App_Data/sent-emails folder instead of sending them using smtp server (recommended for testing purposes only). + /// </summary> + public bool UseLocalFolderForEmails => Host is "LocalFolder"; + + [Range(1, 65535)] + public int Port { get; set; } + public string? UserName { get; set; } + public string? Password { get; set; } + + [Required] + public string DefaultFromEmail { get; set; } = default!; + public bool HasCredential => (string.IsNullOrEmpty(UserName) is false) && (string.IsNullOrEmpty(Password) is false); +} + +public partial class SmsOptions +{ + public string? FromPhoneNumber { get; set; } + public string? TwilioAccountSid { get; set; } + public string? TwilioAutoToken { get; set; } + + public bool Configured => string.IsNullOrEmpty(FromPhoneNumber) is false && + string.IsNullOrEmpty(TwilioAccountSid) is false && + string.IsNullOrEmpty(TwilioAutoToken) is false; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/EmailService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/EmailService.cs index 56f917cd87..8538b0024d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/EmailService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/EmailService.cs @@ -15,7 +15,7 @@ public partial class EmailService [AutoInject] private IHttpContextAccessor httpContextAccessor = default!; [AutoInject] private ILogger<EmailService> logger = default!; [AutoInject] private IHostEnvironment hostEnvironment = default!; - [AutoInject] private AppSettings appSettings = default!; + [AutoInject] private ServerApiSettings appSettings = default!; public async Task SendResetPasswordToken(User user, string token, Uri link, CancellationToken cancellationToken) { @@ -116,7 +116,7 @@ private async Task SendEmail(string body, string toEmailAddress, string toName, { var emailResult = await fluentEmail.To(toEmailAddress, toName) .Subject(subject) - .SetFrom(appSettings.Email.DefaultFromEmail, emailLocalizer[nameof(EmailStrings.DefaultFromName)]) + .SetFrom(appSettings.Email!.DefaultFromEmail, emailLocalizer[nameof(EmailStrings.DefaultFromName)]) .Body(body, isHtml: true) .SendAsync(cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaHttpClient.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaHttpClient.cs index 929414ef98..220fa76873 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaHttpClient.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/GoogleRecaptchaHttpClient.cs @@ -2,11 +2,11 @@ public partial class GoogleRecaptchaHttpClient { - [AutoInject] protected AppSettings AppSettings = default!; + [AutoInject] protected ServerApiSettings AppSettings = default!; [AutoInject] protected HttpClient httpClient = default!; - public async ValueTask<bool> Verify(string? googleRecaptchaResponse, CancellationToken cancellationToken) + public virtual async ValueTask<bool> Verify(string? googleRecaptchaResponse, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(googleRecaptchaResponse)) return false; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs similarity index 98% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs index 9792e886e1..100cd80aad 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs @@ -1,6 +1,4 @@ -using System.Data; - -namespace Boilerplate.Server.Api.Services; +namespace Boilerplate.Server.Api.Services.Identity; public partial class AppIdentityErrorDescriber : IdentityErrorDescriber { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs similarity index 93% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs index 56fe5ecdd7..ce0e33da48 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs @@ -2,12 +2,12 @@ using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Authentication; -namespace Boilerplate.Server.Api.Services; +namespace Boilerplate.Server.Api.Services.Identity; /// <summary> /// Stores bearer token in jwt format /// </summary> -public partial class AppSecureJwtDataFormat(AppSettings appSettings, TokenValidationParameters validationParameters) +public partial class AppSecureJwtDataFormat(ServerApiSettings appSettings, TokenValidationParameters validationParameters) : ISecureDataFormat<AuthenticationTicket> { public AuthenticationTicket? Unprotect(string? protectedText) => Unprotect(protectedText, null); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs similarity index 71% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs index fb8ddb7f98..dda004cc40 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs @@ -1,9 +1,9 @@ using Boilerplate.Server.Api.Models.Identity; -namespace Boilerplate.Server.Api.Services; +namespace Boilerplate.Server.Api.Services.Identity; -public partial class AppUserClaimsPrincipalFactory(UserManager<User> userManager, IOptions<IdentityOptions> optionsAccessor) - : UserClaimsPrincipalFactory<User>(userManager, optionsAccessor) +public partial class AppUserClaimsPrincipalFactory(UserManager<User> userManager, RoleManager<Role> roleManager, IOptions<IdentityOptions> optionsAccessor) + : UserClaimsPrincipalFactory<User, Role>(userManager, roleManager, optionsAccessor) { /// <summary> /// These claims will be included in both the access and refresh tokens only if the successful sign-in happens during the current HTTP request lifecycle. diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs similarity index 84% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs index 14a4f64f87..7113b30613 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs @@ -1,6 +1,6 @@ using Boilerplate.Server.Api.Models.Identity; -namespace Boilerplate.Server.Api.Services; +namespace Boilerplate.Server.Api.Services.Identity; public partial class AppUserConfirmation : IUserConfirmation<User> { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs new file mode 100644 index 0000000000..6fc360a93c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs @@ -0,0 +1,56 @@ +using PhoneNumbers; +using Twilio.Rest.Api.V2010.Account; + +namespace Boilerplate.Server.Api.Services; + +public partial class PhoneService +{ + [AutoInject] private readonly ServerApiSettings appSettings = default!; + [AutoInject] private readonly ILogger<PhoneService> logger = default!; + [AutoInject] private readonly IHostEnvironment hostEnvironment = default!; + [AutoInject] private readonly IHttpContextAccessor httpContextAccessor = default!; + [AutoInject] private readonly PhoneNumberUtil phoneNumberUtil = default!; + + public virtual string? NormalizePhoneNumber(string? phoneNumber) + { + if (string.IsNullOrEmpty(phoneNumber)) + return null; + + // Get region from Cloudflare "CF-IPCountry" header if available, otherwise use UI culture's region if multilingual is enabled, or fallback to the default region. + var region = httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("CF-IPCountry", out var value) + ? value.ToString() + : new RegionInfo((CultureInfoManager.MultilingualEnabled ? CultureInfo.CurrentUICulture : CultureInfoManager.DefaultCulture).Name).TwoLetterISORegionName; + + var parsedPhoneNumber = phoneNumberUtil.Parse(phoneNumber, region); + + return phoneNumberUtil.Format(parsedPhoneNumber, PhoneNumberFormat.E164); + } + + public virtual async Task SendSms(string messageText, string phoneNumber, CancellationToken cancellationToken) + { + if (hostEnvironment.IsDevelopment()) + { + LogSendSms(logger, messageText, phoneNumber); + } + + if (appSettings.Sms?.Configured is false) return; + + var messageOptions = new CreateMessageOptions(new(phoneNumber)) + { + From = new(appSettings.Sms!.FromPhoneNumber), + Body = messageText + }; + + var smsMessage = MessageResource.Create(messageOptions); + + if (smsMessage.ErrorCode is null) return; + + LogSendSmsFailed(logger, phoneNumber, smsMessage.ErrorCode, smsMessage.ErrorMessage); + } + + [LoggerMessage(Level = LogLevel.Information, Message = "SMS: {message} to {phoneNumber}.")] + private static partial void LogSendSms(ILogger logger, string message, string phoneNumber); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to send Sms to {phoneNumber}. Code: {errorCode}, Error message: {errorMessage}")] + private static partial void LogSendSmsFailed(ILogger logger, string phoneNumber, int? errorCode, string errorMessage); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PushNotificationService.cs new file mode 100644 index 0000000000..0b49fd0eae --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PushNotificationService.cs @@ -0,0 +1,102 @@ +using AdsPush; +using AdsPush.Vapid; +using AdsPush.Abstraction; +using System.Linq.Expressions; +using Boilerplate.Shared.Dtos.PushNotification; +using Boilerplate.Server.Api.Models.PushNotification; + +namespace Boilerplate.Server.Api.Services; + +public partial class PushNotificationService +{ + [AutoInject] private IAdsPushSender adsPushSender = default!; + [AutoInject] private IHttpContextAccessor httpContextAccessor = default!; + [AutoInject] private AppDbContext dbContext = default!; + [AutoInject] private ILogger<PushNotificationService> logger = default!; + + public async Task RegisterDevice([Required] DeviceInstallationDto dto, CancellationToken cancellationToken) + { + List<string> tags = [CultureInfo.CurrentUICulture.Name /* To send push notification to all users with specific culture */]; + + var userId = httpContextAccessor.HttpContext!.User.IsAuthenticated() ? httpContextAccessor.HttpContext.User.GetUserId() : (Guid?)null; + + var deviceInstallation = await dbContext.DeviceInstallations.FindAsync([dto.InstallationId], cancellationToken); + + if (deviceInstallation is null) + { + dbContext.DeviceInstallations.Add(deviceInstallation = new() + { + InstallationId = dto.InstallationId, + Platform = dto.Platform + }); + } + + dto.Patch(deviceInstallation); + + deviceInstallation.UserId = userId; + deviceInstallation.Tags = [.. tags]; + deviceInstallation.ExpirationTime = DateTimeOffset.UtcNow.AddMonths(1).ToUnixTimeSeconds(); + + if (deviceInstallation.Platform is "browser") + { + deviceInstallation.PushChannel = VapidSubscription.FromParameters(deviceInstallation.Endpoint, deviceInstallation.P256dh, deviceInstallation.Auth).ToAdsPushToken(); + } + + await dbContext.SaveChangesAsync(cancellationToken); + } + + public async Task DeregisterDevice(string deviceInstallationId, CancellationToken cancellationToken) + { + dbContext.DeviceInstallations.Remove(new() { InstallationId = deviceInstallationId }); + await dbContext.SaveChangesAsync(cancellationToken); + } + + public async Task RequestPush(string? title = null, string? message = null, string? action = null, Expression<Func<DeviceInstallation, bool>>? customDeviceFilter = null, CancellationToken cancellationToken = default) + { + var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + + var query = dbContext.DeviceInstallations + .Where(dev => dev.ExpirationTime > now) + .WhereIf(customDeviceFilter is not null, customDeviceFilter!); + + if (customDeviceFilter is null) + { + query = query.OrderBy(_ => EF.Functions.Random()).Take(100); + } + + var devices = await query.ToListAsync(cancellationToken); + + var payload = new AdsPushBasicSendPayload() + { + Title = AdsPushText.CreateUsingString(title ?? "Boilerplate"), + Detail = AdsPushText.CreateUsingString(message ?? string.Empty), + Parameters = new Dictionary<string, object>() + { + { + "action", action ?? string.Empty + } + } + }; + + List<Task> tasks = []; + + foreach (var deviceInstallation in devices) + { + var target = deviceInstallation.Platform is "browser" ? AdsPushTarget.BrowserAndPwa + : deviceInstallation.Platform is "fcmV1" ? AdsPushTarget.Android + : deviceInstallation.Platform is "apns" ? AdsPushTarget.Ios + : throw new NotImplementedException(); + + tasks.Add(adsPushSender.BasicSendAsync(target, deviceInstallation.PushChannel, payload, cancellationToken)); + } + + try + { + await Task.WhenAll(tasks); + } + catch (Exception exp) + { + logger.LogError(exp, "Failed to send push notification."); + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs index 23e8ea1e32..4cbea7ccfd 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Text.Json; using System.Reflection; using System.Diagnostics; using Microsoft.Net.Http.Headers; @@ -51,10 +50,17 @@ public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception e return true; } - private Exception UnWrapException(Exception exp) + private Exception UnWrapException(Exception exception) { - return exp is TargetInvocationException && exp.InnerException is not null - ? exp.InnerException - : exp; + if (exception is AggregateException aggregateException) + { + return aggregateException.Flatten().InnerException ?? aggregateException; + } + else if (exception is TargetInvocationException) + { + return exception.InnerException ?? exception; + } + + return exception; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs deleted file mode 100644 index f992c9896c..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Twilio.Rest.Api.V2010.Account; - -namespace Boilerplate.Server.Api.Services; - -public partial class SmsService -{ - [AutoInject] private readonly AppSettings appSettings = default!; - [AutoInject] private readonly ILogger<SmsService> logger = default!; - [AutoInject] private readonly IHostEnvironment hostEnvironment = default!; - - public async Task SendSms(string messageText, string phoneNumber, CancellationToken cancellationToken) - { - if (hostEnvironment.IsDevelopment()) - { - LogSendSms(logger, messageText, phoneNumber); - } - - if (appSettings.Sms.Configured is false) return; - - var messageOptions = new CreateMessageOptions(new(phoneNumber)) - { - From = new(appSettings.Sms.FromPhoneNumber), - Body = messageText - }; - - var smsMessage = MessageResource.Create(messageOptions); - - if (smsMessage.ErrorCode is null) return; - - LogSendSmsFailed(logger, phoneNumber, smsMessage.ErrorCode, smsMessage.ErrorMessage); - } - - [LoggerMessage(Level = LogLevel.Information, Message = "SMS: {message} to {phoneNumber}.")] - private static partial void LogSendSms(ILogger logger, string message, string phoneNumber); - - [LoggerMessage(Level = LogLevel.Error, Message = "Failed to send Sms to {phoneNumber}. Code: {errorCode}, Error message: {errorMessage}")] - private static partial void LogSendSmsFailed(ILogger logger, string phoneNumber, int? errorCode, string errorMessage); -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs new file mode 100644 index 0000000000..bdcea9cab4 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Boilerplate.Server.Api.SignalR; + +[AllowAnonymous] +public partial class AppHub : Hub +{ + public override async Task OnConnectedAsync() + { + if (Context.User.IsAuthenticated() is false) + { + if (Context.GetHttpContext()?.Request?.Query?.TryGetValue("access_token", out var _) is true) + { + // AppHub allows anonymous connections. However, if an Authorization is included + // and the user is not authenticated, it indicates the client has sent an invalid or expired access token. + // In this scenario, we should refresh the access token and attempt to reconnect. + + throw new HubException(nameof(AppStrings.UnauthorizedException)); + } + } + else + { + await Groups.AddToGroupAsync(Context.ConnectionId, "AuthenticatedClients"); + } + + await base.OnConnectedAsync(); + } + + public override async Task OnDisconnectedAsync(Exception? exception) + { + await Groups.RemoveFromGroupAsync(Context.ConnectionId, "AuthenticatedClients"); + + await base.OnDisconnectedAsync(exception); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Development.json index dae7ee35bf..211cab3b63 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Development.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Development.json @@ -1,3 +1,4 @@ { - "DetailedErrors": true + "DetailedErrors": true, + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Production.json index a27ee45b6c..2b7a979354 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Production.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.Production.json @@ -1,6 +1,3 @@ { - "ForwardedHeaders": { - "AllowedHosts": "use-your-server-url-here.com", - "AllowedHosts_Comment": "If the list is empty then all hosts are allowed. Failing to restrict this these values may allow an attacker to spoof links generated for reset password etc." - } + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json index 4cdba1f80a..7aa6759d8a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json @@ -18,61 +18,92 @@ //#endif }, "DataProtectionCertificatePassword": "P@ssw0rdP@ssw0rd", - "DataProtectionCertificatePassword_Comment": "It can also be configured using: dotnet user-secrets set 'DataProtectionCertificatePassword' 'P@ssw0rdP@ssw0rd'", - "AppSettings": { - "Identity": { - "Issuer": "Boilerplate", - "Audience": "Boilerplate", - "BearerTokenExpiration": "0.00:05:00", - "BearerTokenExpiration_Comment": "BearerTokenExpiration used as jwt's expiration claim, access token's expires in and cookie's max age. Format: D.HH:mm:ss", - "RefreshTokenExpiration": "14.00:00:00", - "EmailTokenLifetime": "0.00:02:00", - "PhoneNumberTokenLifetime": "0.00:02:00", - "ResetPasswordTokenLifetime": "0.00:02:00", - "TwoFactorTokenLifetime": "0.00:02:00", - "OtpTokenLifetime": "0.00:02:00", - "RevokeUserSessionsDelay": "1.00:00:00", - "Password": { - "RequireDigit": "false", - "RequiredLength": "6", - "RequireNonAlphanumeric": "false", - "RequireUppercase": "false", - "RequireLowercase": "false" - }, - "SignIn": { - "RequireConfirmedAccount": true - } + "DataProtectionCertificatePassword_Comment": "It can also be configured using: dotnet user-secrets set 'DataProtectionCertificatePassword' '@nyPassw0rd'", + "Identity": { + "Issuer": "Boilerplate", + "Audience": "Boilerplate", + "BearerTokenExpiration": "0.00:05:00", + "BearerTokenExpiration_Comment": "BearerTokenExpiration used as JWT's expiration claim, access token's expires in and cookie's max age. Format: D.HH:mm:ss", + "RefreshTokenExpiration": "14.00:00:00", + "EmailTokenLifetime": "0.00:02:00", + "PhoneNumberTokenLifetime": "0.00:02:00", + "ResetPasswordTokenLifetime": "0.00:02:00", + "TwoFactorTokenLifetime": "0.00:02:00", + "OtpTokenLifetime": "0.00:02:00", + "RevokeUserSessionsDelay": "1.00:00:00", + "Password": { + "RequireDigit": "false", + "RequiredLength": "6", + "RequireNonAlphanumeric": "false", + "RequireUppercase": "false", + "RequireLowercase": "false" }, - "Email": { - "Host": "LocalFolder", - "Host_Comment": "Local folder means storing emails as .eml file in App_Data/sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server.", - "Port": "587", - "DefaultFromEmail": "info@Boilerplate.com", - "UserName": null, - "Password": null - }, - "Sms": { - "FromPhoneNumber": null, - "TwilioAccountSid": null, - "TwilioAutoToken": null - }, - "UserProfileImagesDir": "attachments/profiles/", - //#if (captcha == "reCaptcha") - "GoogleRecaptchaSecretKey": "6LdMKr4pAAAAANvngWNam_nlHzEDJ2t6SfV6L_DS" - //#endif + "SignIn": { + "RequireConfirmedAccount": true + } + }, + "Email": { + "Host": "LocalFolder", + "Host_Comment": "Local folder means storing emails as .eml file in App_Data/sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server.", + "Port": "587", + "DefaultFromEmail": "info@Boilerplate.com", + "UserName": null, + "Password": null }, + "Sms": { + "FromPhoneNumber": null, + "TwilioAccountSid": null, + "TwilioAutoToken": null + }, + "UserProfileImagesDir": "attachments/profiles/", + //#if (captcha == "reCaptcha") + "GoogleRecaptchaSecretKey": "6LdMKr4pAAAAANvngWNam_nlHzEDJ2t6SfV6L_DS", + //#endif + //#if (notification == true) + "AdsPushVapid_Comment": "https://github.com/adessoTurkey-dotNET/AdsPush", + "AdsPushVapid": { + "AdsPushVapid_Comment": "Web push's vapid. More info at https://vapidkeys.com/", + "Subject": "mailto: <test@bitplatform.dev>", + "PrivateKey": "dMIR1ICj-lDWYZ-ZYCwXKyC2ShYayYYkEL-oOPnpq9c", + "PublicKey_Comment": "Set public key in Client.Core's appsettings.json" + }, + "AdsPushAPNS": { + "P8PrivateKey": null, + "P8PrivateKey_Comment": "p8 certificate string without spaces and start/end tags.", + "P8PrivateKeyId": null, + "P8PrivateKeyId_Comment": "10-digit p8 certificate id; often part of a downloadable certificate filename", + "TeamId": null, + "TeamId_Comment": "10-digit Apple team id shown on the Apple Developer Membership Page", + "AppBundleIdentifier": null, + "EnvironmentType": "Development", + "EnvironmentType_Comment": "Apns Env one of Development or Production" + }, + "AdsPushFirebase": { + "AdsPushFirebase_Comment": "Filed names in service_account.json => project_id,private_key_id,private_key,client_email,client_id,client_x509_cert_url", + "Type": "service_account", + "AuthUri": "https://accounts.google.com/o/oauth2/auth", + "TokenUri": "https://oauth2.googleapis.com/token", + "AuthProviderX509CertUrl": "https://www.googleapis.com/oauth2/v1/certs", + "ProjectId": null, + "PrivateKeyId": null, + "PrivateKey": null, + "ClientEmail": null, + "ClientId": null, + "ClientX509CertUrl": null + }, + //#endif "Authentication": { "Google": { - "ClientId": "", - "ClientSecret": "" + "ClientId": null, + "ClientSecret": null }, "GitHub": { - "ClientId": "", - "ClientSecret": "" + "ClientId": null, + "ClientSecret": null }, "Twitter": { - "ConsumerKey": "", - "ConsumerSecret": "" + "ConsumerKey": null, + "ConsumerSecret": null } }, "AllowedHosts": "*", @@ -80,8 +111,7 @@ "ForwardedHeaders_Comment": "These values apply only if your backend is hosted behind a CDN (such as Cloudflare).", "ForwardedHostHeaderName": "X-Forwarded-Host", "ForwardedHostHeaderName_Comment": "For Cloudflare, use X-Host instead of X-Forwarded-Host.", - "ForwardedHeaders": "All", - "AllowedHosts": "*", - "AllowedHosts_Comment": "Configure this in production with your backend URL host address (See appsettings.Production.json)" - } + "ForwardedHeaders": "All" + }, + "$schema": "https://json.schemastore.org/appsettings.json" } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json new file mode 100644 index 0000000000..40d4373bd4 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "8.0.10", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj index 3f28d2d36e..813308ddd4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj @@ -7,12 +7,10 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> - <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8" PrivateAssets="all" /> - <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8" PrivateAssets="all" /> - <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" /> + <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Tools" PrivateAssets="all" /> + <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" /> + <PackageReference Condition=" '$(appInsights)' == 'true' OR '$(appInsights)' == '' " Include="Microsoft.ApplicationInsights.AspNetCore" /> </ItemGroup> <ItemGroup> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor index 1a6905d002..5204de1499 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor @@ -3,7 +3,7 @@ @{ var noPrerender = HttpContext.Request.Query["no-prerender"].Count > 0; - var renderMode = noPrerender ? AppRenderMode.NoPrerenderBlazorWebAssembly : AppRenderMode.Current; + var renderMode = noPrerender ? noPrerenderBlazorWebAssembly : serverWebSettings.WebAppRender.RenderMode; } <!DOCTYPE html> @@ -14,13 +14,13 @@ <meta charset="utf-8" /> <meta name="theme-color"> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <meta name="description" content="@localizer[nameof(AppStrings.DescriptionMetaTagValue)]" /> <Script> // disable auto-zoom of iOS Safari when focusing an input (/iPad|iPhone|iPod/.test(navigator.userAgent)) && - (document.querySelector('meta[name="viewport"]').content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0') + (document.querySelector('meta[name="viewport"]').content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover') </Script> @*#if (captcha == "reCaptcha")*@ @@ -32,8 +32,12 @@ <Link rel="icon" href="favicon.ico" type="image/x-icon" /> <HeadOutlet @rendermode=renderMode /> + @*#if (appInsights == true)*@ + <Link rel="preconnect" href="https://js.monitor.azure.com" crossorigin /> + <BlazorApplicationInsights.ApplicationInsightsInit @rendermode=renderMode /> + @*#endif*@ <Link rel="apple-touch-icon" sizes="512x512" href="images/icons/bit-icon-512.png" /> - @if (AppRenderMode.PwaEnabled) + @if (serverWebSettings.WebAppRender.PwaEnabled) { <Link rel="manifest" href="manifest.json" /> } @@ -46,9 +50,9 @@ <Link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" /> <Link rel="stylesheet" href="_content/Boilerplate.Client.Core/styles/app.css" /> - <Link rel="stylesheet" href="_content/Boilerplate.Client.Core/Boilerplate.Client.Core.bundle.scp.css" /> + <Link rel="stylesheet" href="Boilerplate.Server.Web.styles.css" /> - @if (AppRenderMode.PrerenderEnabled is false || noPrerender) + @if (renderMode != null && (serverWebSettings.WebAppRender.PrerenderEnabled is false || noPrerender)) { <LoadingComponent /> } @@ -57,8 +61,10 @@ @if (HttpContext.Request.IsCrawlerClient() is false) { - <Script src="_framework/blazor.web.js" autostart="false"></Script> - @if (AppRenderMode.PwaEnabled) + <Script src="_framework/blazor.web.js?ver=8.0.404" autostart="false"></Script> + <!-- Ensure that the version of `blazor.web.js` matches the version specified in `service-worker.published.js`. + This alignment is necessary for the PWA functionality of the Blazor app to work correctly. --> + @if (serverWebSettings.WebAppRender.PwaEnabled) { <Script src="_content/Bit.Bswup/bit-bswup.js"></Script> <Script src="_content/Bit.Bswup/bit-bswup.progress.js"></Script> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor.cs index 98c934869a..1d08359c5d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Components/App.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Localization; namespace Boilerplate.Server.Web.Components; @@ -6,9 +7,12 @@ namespace Boilerplate.Server.Web.Components; [StreamRendering(enabled: true)] public partial class App { + private static readonly IComponentRenderMode noPrerenderBlazorWebAssembly = new InteractiveWebAssemblyRenderMode(prerender: false); + [CascadingParameter] HttpContext HttpContext { get; set; } = default!; [AutoInject] IStringLocalizer<AppStrings> localizer = default!; + [AutoInject] ServerWebSettings serverWebSettings = default!; protected override void OnInitialized() { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs index d84a09788c..8f2c0376ff 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs @@ -1,7 +1,6 @@ //+:cnd:noEmit using System.Net; using System.Web; -using System.Reflection; using System.Runtime.Loader; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.FileProviders; @@ -9,7 +8,7 @@ using Microsoft.AspNetCore.Components.Endpoints; using Microsoft.AspNetCore.Localization.Routing; using Boilerplate.Shared; -using Boilerplate.Client.Core.Services; + namespace Boilerplate.Server.Web; @@ -18,12 +17,19 @@ public static partial class Program /// <summary> /// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0#middleware-order /// </summary> - private static void ConfiureMiddlewares(this WebApplication app) + public static void ConfigureMiddlewares(this WebApplication app) { var configuration = app.Configuration; var env = app.Environment; - app.UseForwardedHeaders(); + var forwarededHeadersOptions = configuration.Get<ServerWebSettings>()!.ForwardedHeaders; + + if (forwarededHeadersOptions is not null + && (app.Environment.IsDevelopment() || forwarededHeadersOptions.AllowedHosts.Any())) + { + // If the list is empty then all hosts are allowed. Failing to restrict this these values may allow an attacker to spoof links generated for reset password etc. + app.UseForwardedHeaders(forwarededHeadersOptions); + } if (CultureInfoManager.MultilingualEnabled) { @@ -116,22 +122,24 @@ private static void ConfiureMiddlewares(this WebApplication app) QueryStringParameter = queryStringParameter }).WithTags("Test"); - //#if (signalr == true) - app.MapHub<Api.Hubs.AppHub>("/app-hub"); + //#if (signalR == true) + app.MapHub<Api.SignalR.AppHub>("/app-hub"); //#endif app.MapControllers().RequireAuthorization(); //#endif app.UseSiteMap(); - + // Handle the rest of requests with blazor var blazorApp = app.MapRazorComponents<Components.App>() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() - .AddAdditionalAssemblies(AssemblyLoadContext.Default.Assemblies.Where(asm => asm.GetName().Name?.Contains("Boilerplate") is true).Except([Assembly.GetExecutingAssembly()]).ToArray()); + .AddAdditionalAssemblies(AssemblyLoadContext.Default.Assemblies.Where(asm => asm.GetName().Name?.Contains("Boilerplate.Client") is true).ToArray()); + + var webAppRenderMode = configuration.Get<ServerWebSettings>()!; - if (AppRenderMode.PrerenderEnabled is false) + if (webAppRenderMode.WebAppRender.PrerenderEnabled is false) { blazorApp.AllowAnonymous(); // Server may not check authorization for pages when there's no pre rendering, let the client handle it. } @@ -139,14 +147,11 @@ private static void ConfiureMiddlewares(this WebApplication app) private static void UseSiteMap(this WebApplication app) { - var urls = typeof(Urls) - .GetFields() - .Select(f => f.GetValue(null)!.ToString()!) - .ToList()!; + var urls = Urls.All!; urls = CultureInfoManager.MultilingualEnabled ? - urls.Union(CultureInfoManager.SupportedCultures.SelectMany(sc => urls.Select(url => $"{sc.Culture.Name}{url}"))).ToList() : - urls; + urls.Union(CultureInfoManager.SupportedCultures.SelectMany(sc => urls.Select(url => $"{sc.Culture.Name}{url}"))).ToArray() : + urls; const string siteMapHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset\r\n xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\r\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs index 954fc70135..876f94037b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs @@ -1,7 +1,6 @@ //+:cnd:noEmit using System.IO.Compression; using Microsoft.Net.Http.Headers; -using Microsoft.Extensions.Options; using Microsoft.AspNetCore.ResponseCompression; //#if (api == "Integrated") using Boilerplate.Server.Api; @@ -14,22 +13,24 @@ namespace Boilerplate.Server.Web; public static partial class Program { - private static void ConfigureServices(this WebApplicationBuilder builder) + public static void AddServerWebProjectServices(this WebApplicationBuilder builder) { // Services being registered here can get injected in server project only. - var services = builder.Services; var configuration = builder.Configuration; - AddBlazor(builder); + if (AppEnvironment.IsDev()) + { + builder.Logging.AddDiagnosticLogger(); + } + + services.AddClientWebProjectServices(configuration); + + services.AddSingleton(sp => configuration.Get<ServerWebSettings>()!); //#if (api == "Integrated") - builder.ConfigureApiServices(); + builder.AddServerApiProjectServices(); //#else - services.AddOptions<ForwardedHeadersOptions>() - .Bind(configuration.GetRequiredSection("ForwardedHeaders")) - .ValidateDataAnnotations() - .ValidateOnStart(); services.AddResponseCaching(); @@ -52,7 +53,12 @@ private static void ConfigureServices(this WebApplicationBuilder builder) services.AddAntiforgery(); //#endif - services.AddClientWebProjectServices(); + services.AddOptions<ServerWebSettings>() + .Bind(configuration) + .ValidateDataAnnotations() + .ValidateOnStart(); + + AddBlazor(builder); } private static void AddBlazor(WebApplicationBuilder builder) @@ -60,9 +66,8 @@ private static void AddBlazor(WebApplicationBuilder builder) var services = builder.Services; var configuration = builder.Configuration; - services.TryAddTransient<IAuthTokenProvider, ServerSideAuthTokenProvider>(); - - services.TryAddTransient(sp => + services.AddScoped<IAuthTokenProvider, ServerSideAuthTokenProvider>(); + services.AddScoped(sp => { // This HTTP client is utilized during pre-rendering and within Blazor Auto/Server sessions for API calls. // Key headers such as Authorization and AcceptLanguage headers are added in Client/Core/Services/HttpMessageHandlers. @@ -76,12 +81,12 @@ private static void AddBlazor(WebApplicationBuilder builder) serverAddress = new Uri(currentRequest.GetBaseUrl(), serverAddress); } - var httpClient = new HttpClient(sp.GetRequiredKeyedService<DelegatingHandler>("DefaultMessageHandler")) + var httpClient = new HttpClient(sp.GetRequiredService<HttpMessageHandler>()) { BaseAddress = serverAddress }; - var forwardedHeadersOptions = sp.GetRequiredService<IOptionsSnapshot<ForwardedHeadersOptions>>().Value; + var forwardedHeadersOptions = sp.GetRequiredService<ServerWebSettings>().ForwardedHeaders; foreach (var xHeader in currentRequest.Headers.Where(h => h.Key.StartsWith("X-", StringComparison.InvariantCultureIgnoreCase))) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.cs index dce3cdd639..a762339406 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.cs @@ -19,20 +19,20 @@ public static async Task Main(string[] args) AppEnvironment.Set(builder.Environment.EnvironmentName); - builder.Configuration.AddClientConfigurations(); + builder.Configuration.AddClientConfigurations(clientEntryAssemblyName: "Boilerplate.Client.Web"); // The following line (using the * in the URL), allows the emulators and mobile devices to access the app using the host IP address. - if (AppEnvironment.IsDev() && OperatingSystem.IsWindows()) + if (builder.Environment.IsDevelopment() && OperatingSystem.IsWindows()) { builder.WebHost.UseUrls("http://localhost:5030", "http://*:5030"); } - builder.ConfigureServices(); + builder.AddServerWebProjectServices(); var app = builder.Build(); //#if (api == "Integrated") - if (AppEnvironment.IsDev()) + if (builder.Environment.IsDevelopment()) { await using var scope = app.Services.CreateAsyncScope(); var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); @@ -40,7 +40,7 @@ public static async Task Main(string[] args) } //#endif - app.ConfiureMiddlewares(); + app.ConfigureMiddlewares(); await app.RunAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Properties/launchSettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Properties/launchSettings.json index f5492bfca1..3a79b09dd3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Properties/launchSettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Properties/launchSettings.json @@ -25,7 +25,8 @@ "commandName": "Project", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "WebAppRender:BlazorMode": "BlazorWebAssembly" }, "dotnetRunMessages": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", @@ -46,6 +47,7 @@ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/", "environmentVariables": { "ASPNETCORE_HTTP_PORTS": "5030", + "ASPNETCORE_ENVIRONMENT": "Development", "ConnectionStrings__SqliteConnectionString": "Data Source=/container_volume/App_Data/BoilerplateDb.db;" }, "DockerfileRunArguments": "-v C:\\DockerVolumes\\AC87AA5B-4B37-4E52-8468-2D5DF24AF256:/container_volume", diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/ServerWebSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/ServerWebSettings.cs new file mode 100644 index 0000000000..70631e218f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/ServerWebSettings.cs @@ -0,0 +1,18 @@ +//+:cnd:noEmit +using Boilerplate.Client.Web; + +namespace Boilerplate.Server.Web; + +public partial class ServerWebSettings : ClientWebSettings +{ + public ForwardedHeadersOptions ForwardedHeaders { get; set; } = default!; + + public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = base.Validate(validationContext).ToList(); + + Validator.TryValidateObject(ForwardedHeaders, new ValidationContext(ForwardedHeaders), validationResults, true); + + return validationResults; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Services/ServerSideAuthTokenProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Services/ServerSideAuthTokenProvider.cs index c523e59b55..bf61f8bedd 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Services/ServerSideAuthTokenProvider.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Services/ServerSideAuthTokenProvider.cs @@ -16,11 +16,9 @@ public partial class ServerSideAuthTokenProvider : IAuthTokenProvider [AutoInject] private IStorageService storageService = default!; [AutoInject] private IHttpContextAccessor httpContextAccessor = default!; - public bool IsInitialized => jsRuntime.GetType().Name is not "UnsupportedJavaScriptRuntime" && jsRuntime.IsInitialized(); - - public async Task<string?> GetAccessTokenAsync() + public async Task<string?> GetAccessToken() { - if (IsInitialized) + if (jsRuntime.IsInitialized()) { return await storageService.GetItem("access_token"); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Development.json index dae7ee35bf..211cab3b63 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Development.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Development.json @@ -1,3 +1,4 @@ { - "DetailedErrors": true + "DetailedErrors": true, + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Production.json index a27ee45b6c..2b7a979354 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Production.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.Production.json @@ -1,6 +1,3 @@ { - "ForwardedHeaders": { - "AllowedHosts": "use-your-server-url-here.com", - "AllowedHosts_Comment": "If the list is empty then all hosts are allowed. Failing to restrict this these values may allow an attacker to spoof links generated for reset password etc." - } + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json index 2ca13f26cc..ca4aaf224e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json @@ -12,58 +12,87 @@ }, "DataProtectionCertificatePassword": "P@ssw0rdP@ssw0rd", "DataProtectionCertificatePassword_Comment": "It can also be configured using: dotnet user-secrets set 'DataProtectionCertificatePassword' 'P@ssw0rdP@ssw0rd'", - "AppSettings": { - "Identity": { - "Issuer": "Boilerplate", - "Audience": "Boilerplate", - "BearerTokenExpiration": "0.00:05:00", - "BearerTokenExpiration_Comment": "BearerTokenExpiration used as jwt's expiration claim, access token's expires in and cookie's max age. Format: D.HH:mm:ss", - "RefreshTokenExpiration": "14.00:00:00", - "EmailTokenLifetime": "0.00:02:00", - "PhoneNumberTokenLifetime": "0.00:02:00", - "ResetPasswordTokenLifetime": "0.00:02:00", - "TwoFactorTokenLifetime": "0.00:02:00", - "OtpTokenLifetime": "0.00:02:00", - "RevokeUserSessionsDelay": "1.00:00:00", - "Password": { - "RequireDigit": "false", - "RequiredLength": "6", - "RequireNonAlphanumeric": "false", - "RequireUppercase": "false", - "RequireLowercase": "false" - }, - "SignIn": { - "RequireConfirmedAccount": true - } + "Identity": { + "Issuer": "Boilerplate", + "Audience": "Boilerplate", + "BearerTokenExpiration": "0.00:05:00", + "BearerTokenExpiration_Comment": "BearerTokenExpiration used as JWT's expiration claim, access token's expires in and cookie's max age. Format: D.HH:mm:ss", + "RefreshTokenExpiration": "14.00:00:00", + "EmailTokenLifetime": "0.00:02:00", + "PhoneNumberTokenLifetime": "0.00:02:00", + "ResetPasswordTokenLifetime": "0.00:02:00", + "TwoFactorTokenLifetime": "0.00:02:00", + "OtpTokenLifetime": "0.00:02:00", + "RevokeUserSessionsDelay": "1.00:00:00", + "Password": { + "RequireDigit": "false", + "RequiredLength": "6", + "RequireNonAlphanumeric": "false", + "RequireUppercase": "false", + "RequireLowercase": "false" }, - "Email": { - "Host": "LocalFolder", - "Host_Comment": "Local folder means storing emails as .eml file in App_Data/sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server.", - "Port": "587", - "DefaultFromEmail": "info@Boilerplate.com", - "UserName": null, - "Password": null - }, - "Sms": { - "FromPhoneNumber": null, - "TwilioAccountSid": null, - "TwilioAutoToken": null - }, - "UserProfileImagesDir": "attachments/profiles/", - "GoogleRecaptchaSecretKey": "6LdMKr4pAAAAANvngWNam_nlHzEDJ2t6SfV6L_DS" + "SignIn": { + "RequireConfirmedAccount": true + } + }, + "Email": { + "Host": "LocalFolder", + "Host_Comment": "Local folder means storing emails as .eml file in App_Data/sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server.", + "Port": "587", + "DefaultFromEmail": "info@Boilerplate.com", + "UserName": null, + "Password": null + }, + "Sms": { + "FromPhoneNumber": null, + "TwilioAccountSid": null, + "TwilioAutoToken": null + }, + "UserProfileImagesDir": "attachments/profiles/", + "GoogleRecaptchaSecretKey": "6LdMKr4pAAAAANvngWNam_nlHzEDJ2t6SfV6L_DS", + "AdsPushVapid_Comment": "https://github.com/adessoTurkey-dotNET/AdsPush", + "AdsPushVapid": { + "AdsPushVapid_Comment": "Web push's vapid. More info at https://vapidkeys.com/", + "Subject": "mailto: <test@bitplatform.dev>", + "PrivateKey": "dMIR1ICj-lDWYZ-ZYCwXKyC2ShYayYYkEL-oOPnpq9c", + "PublicKey_Comment": "Set public key in Client.Core's appsettings.json" + }, + "AdsPushAPNS": { + "P8PrivateKey": null, + "P8PrivateKey_Comment": "p8 certificate string without spaces and start/end tags.", + "P8PrivateKeyId": null, + "P8PrivateKeyId_Comment": "10-digit p8 certificate id; often part of a downloadable certificate filename", + "TeamId": null, + "TeamId_Comment": "10-digit Apple team id shown on the Apple Developer Membership Page", + "AppBundleIdentifier": null, + "EnvironmentType": "Development", + "EnvironmentType_Comment": "Apns Env one of Development or Production" + }, + "AdsPushFirebase": { + "AdsPushFirebase_Comment": "Filed names in service_account.json => project_id,private_key_id,private_key,client_email,client_id,client_x509_cert_url", + "Type": "service_account", + "AuthUri": "https://accounts.google.com/o/oauth2/auth", + "TokenUri": "https://oauth2.googleapis.com/token", + "AuthProviderX509CertUrl": "https://www.googleapis.com/oauth2/v1/certs", + "ProjectId": null, + "PrivateKeyId": null, + "PrivateKey": null, + "ClientEmail": null, + "ClientId": null, + "ClientX509CertUrl": null }, "Authentication": { "Google": { - "ClientId": "", - "ClientSecret": "" + "ClientId": null, + "ClientSecret": null }, "GitHub": { - "ClientId": "", - "ClientSecret": "" + "ClientId": null, + "ClientSecret": null }, "Twitter": { - "ConsumerKey": "", - "ConsumerSecret": "" + "ConsumerKey": null, + "ConsumerSecret": null } }, //#endif @@ -72,8 +101,7 @@ "ForwardedHeaders_Comment": "These values apply only if your backend is hosted behind a CDN (such as Cloudflare).", "ForwardedHostHeaderName": "X-Forwarded-Host", "ForwardedHostHeaderName_Comment": "For Cloudflare, use X-Host instead of X-Forwarded-Host.", - "ForwardedHeaders": "All", - "AllowedHosts": "*", - "AllowedHosts_Comment": "Configure this in production with your backend URL host address (See appsettings.Production.json)" - } + "ForwardedHeaders": "All" + }, + "$schema": "https://json.schemastore.org/appsettings.json" } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Boilerplate.Shared.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Boilerplate.Shared.csproj index 7b3a8d9ecf..d91b299f5b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Boilerplate.Shared.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Boilerplate.Shared.csproj @@ -6,24 +6,16 @@ </PropertyGroup> <ItemGroup> - <Content Remove="appsettings*.json" /> - <EmbeddedResource Include="appsettings*.json" /> - <EmbeddedResource Update="appsettings.*.json"> - <DependentUpon>appsettings.json</DependentUpon> - </EmbeddedResource> - </ItemGroup> - - <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0" PrivateAssets="all" /> - <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> - <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.8" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" /> - <PackageReference Include="System.Text.Json" Version="8.0.4" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" PrivateAssets="all" ExcludeAssets="runtime"> + <PackageReference Include="Microsoft.AspNetCore.Authorization" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.Localization" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" /> + <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" /> + <PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" /> + <PackageReference Include="System.Text.Json" /> + <PackageReference Include="Riok.Mapperly" PrivateAssets="all" ExcludeAssets="runtime"> <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> @@ -44,6 +36,9 @@ <StronglyTypedClassName>AppStrings</StronglyTypedClassName> <PublicClass>true</PublicClass> </EmbeddedResource> + <EmbeddedResource Update="Resources\AppStrings.*.resx"> + <DependentUpon>Resources\AppStrings.resx</DependentUpon> + </EmbeddedResource> <EmbeddedResource Update="Resources\IdentityStrings.resx"> <Generator>MSBuild:Compile</Generator> <LastGenOutput>Resources\IdentityStrings.Designer.cs</LastGenOutput> @@ -53,6 +48,20 @@ <StronglyTypedClassName>IdentityStrings</StronglyTypedClassName> <PublicClass>true</PublicClass> </EmbeddedResource> + <EmbeddedResource Update="Resources\IdentityStrings.*.resx"> + <DependentUpon>Resources\IdentityStrings.resx</DependentUpon> + </EmbeddedResource> + </ItemGroup> + + <ItemGroup> + <Content Remove="appsettings*.json" /> + <EmbeddedResource Include="appsettings*.json" /> + <EmbeddedResource Update="appsettings.*.json"> + <DependentUpon>appsettings.json</DependentUpon> + </EmbeddedResource> + <Compile Update="Urls.*.cs"> + <DependentUpon>Urls.cs</DependentUpon> + </Compile> </ItemGroup> </Project> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs index c3db0748a0..e05c46bb5a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs @@ -14,8 +14,8 @@ public interface ICategoryController : IAppController [HttpPut] Task<CategoryDto> Update(CategoryDto dto, CancellationToken cancellationToken); - [HttpDelete("{id}")] - Task Delete(Guid id, CancellationToken cancellationToken); + [HttpDelete("{id}/{concurrencyStamp}")] + Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken); [HttpGet] Task<PagedResult<CategoryDto>> GetCategories(CancellationToken cancellationToken) => default!; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Dashboard/IDashboardController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Dashboard/IDashboardController.cs index bda81bd93b..a238327a30 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Dashboard/IDashboardController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Dashboard/IDashboardController.cs @@ -11,9 +11,6 @@ public interface IDashboardController : IAppController [HttpGet] Task<List<ProductsCountPerCategoryResponseDto>> GetProductsCountPerCategoryStats(CancellationToken cancellationToken) => default!; - [HttpGet] - Task<List<ProductSaleStatResponseDto>> GetProductsSalesStats(CancellationToken cancellationToken) => default!; - [HttpGet] Task<List<ProductPercentagePerCategoryResponseDto>> GetProductsPercentagePerCategoryStats(CancellationToken cancellationToken); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/IMinimalApiController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/IMinimalApiController.cs index 12c62f7f4c..3ce8c5b362 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/IMinimalApiController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/IMinimalApiController.cs @@ -1,9 +1,7 @@ -using System.Text.Json; - -namespace Boilerplate.Shared.Controllers; +namespace Boilerplate.Shared.Controllers; public interface IMinimalApiController : IAppController { [HttpGet("api/minimal-api-sample/{routeParameter}{?queryStringParameter}")] - Task<JsonElement> MinimalApiSample(string routeParameter, string queryStringParameter, CancellationToken cancellationToken); + Task<JsonElement> MinimalApiSample(string routeParameter, string? queryStringParameter, CancellationToken cancellationToken); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IIdentityController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IIdentityController.cs index 761614b1c2..653e263da7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IIdentityController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IIdentityController.cs @@ -24,6 +24,7 @@ public interface IIdentityController : IAppController [HttpPost] Task ResetPassword(ResetPasswordRequestDto request, CancellationToken cancellationToken); + public const string RefreshUri = "api/Identity/Refresh"; [HttpPost] Task<TokenResponseDto> Refresh(RefreshRequestDto request, CancellationToken cancellationToken) => default!; @@ -37,7 +38,7 @@ public interface IIdentityController : IAppController Task<SignInResponseDto> SignIn(SignInRequestDto request, CancellationToken cancellationToken) => default!; [HttpPost] - Task SendTwoFactorToken(IdentityRequestDto request, CancellationToken cancellationToken); + Task SendTwoFactorToken(SignInRequestDto request, CancellationToken cancellationToken); [HttpPost("{?returnUrl}")] Task SendOtp(IdentityRequestDto request, string? returnUrl = null, CancellationToken cancellationToken = default); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs index 981df889d8..0d915c8e2b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs @@ -1,5 +1,4 @@ using Boilerplate.Shared.Dtos.Identity; -using Boilerplate.Shared.Resources; namespace Boilerplate.Shared.Controllers.Identity; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs index b7905d59dc..669f71e376 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs @@ -14,8 +14,8 @@ public interface IProductController : IAppController [HttpPut] Task<ProductDto> Update(ProductDto dto, CancellationToken cancellationToken); - [HttpDelete("{id}")] - Task Delete(Guid id, CancellationToken cancellationToken); + [HttpDelete("{id}/{concurrencyStamp}")] + Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken); [HttpGet] Task<PagedResult<ProductDto>> GetProducts(CancellationToken cancellationToken) => default!; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/PushNotification/IPushNotificationController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/PushNotification/IPushNotificationController.cs new file mode 100644 index 0000000000..f61ea747fe --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/PushNotification/IPushNotificationController.cs @@ -0,0 +1,13 @@ +using Boilerplate.Shared.Dtos.PushNotification; + +namespace Boilerplate.Shared.Controllers.PushNotification; + +[Route("api/[controller]/[action]/")] +public interface IPushNotificationController : IAppController +{ + [HttpPost] + Task RegisterDevice([Required] DeviceInstallationDto deviceInstallation, CancellationToken cancellationToken); + + [HttpPost("{deviceId}")] + Task DeregisterDevice([Required] string deviceId, CancellationToken cancellationToken); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs index 2a84ad4362..6ab5f7d91c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs @@ -6,7 +6,9 @@ using Boilerplate.Shared.Dtos.Dashboard; using Boilerplate.Shared.Dtos.Products; //#endif -using Boilerplate.Shared.Dtos.Identity; +//#if (notification == true) +using Boilerplate.Shared.Dtos.PushNotification; +//#endif namespace Boilerplate.Shared.Dtos; @@ -14,8 +16,12 @@ namespace Boilerplate.Shared.Dtos; /// https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/ /// </summary> [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] -[JsonSerializable(typeof(Dictionary<string, object>))] -[JsonSerializable(typeof(UserDto))] +[JsonSerializable(typeof(Dictionary<string, JsonElement>))] +[JsonSerializable(typeof(string[]))] +[JsonSerializable(typeof(RestErrorInfo))] +//#if (notification == true) +[JsonSerializable(typeof(DeviceInstallationDto))] +//#endif //#if (sample == "Todo") [JsonSerializable(typeof(TodoItemDto))] [JsonSerializable(typeof(PagedResult<TodoItemDto>))] @@ -23,7 +29,6 @@ namespace Boilerplate.Shared.Dtos; //#elif (sample == "Admin") [JsonSerializable(typeof(List<ProductsCountPerCategoryResponseDto>))] [JsonSerializable(typeof(OverallAnalyticsStatsDataResponseDto))] -[JsonSerializable(typeof(List<ProductSaleStatResponseDto>))] [JsonSerializable(typeof(List<ProductPercentagePerCategoryResponseDto>))] [JsonSerializable(typeof(ProductDto))] [JsonSerializable(typeof(PagedResult<ProductDto>))] @@ -32,25 +37,6 @@ namespace Boilerplate.Shared.Dtos; [JsonSerializable(typeof(PagedResult<CategoryDto>))] [JsonSerializable(typeof(List<CategoryDto>))] //#endif -[JsonSerializable(typeof(IdentityRequestDto))] -[JsonSerializable(typeof(SignInRequestDto))] -[JsonSerializable(typeof(SignInResponseDto))] -[JsonSerializable(typeof(TokenResponseDto))] -[JsonSerializable(typeof(RefreshRequestDto))] -[JsonSerializable(typeof(SignUpRequestDto))] -[JsonSerializable(typeof(EditUserDto))] -[JsonSerializable(typeof(RestErrorInfo))] -[JsonSerializable(typeof(SendEmailTokenRequestDto))] -[JsonSerializable(typeof(SendPhoneTokenRequestDto))] -[JsonSerializable(typeof(ConfirmEmailRequestDto))] -[JsonSerializable(typeof(ChangeEmailRequestDto))] -[JsonSerializable(typeof(ConfirmPhoneRequestDto))] -[JsonSerializable(typeof(ChangePhoneNumberRequestDto))] -[JsonSerializable(typeof(SendResetPasswordTokenRequestDto))] -[JsonSerializable(typeof(ResetPasswordRequestDto))] -[JsonSerializable(typeof(TwoFactorAuthRequestDto))] -[JsonSerializable(typeof(TwoFactorAuthResponseDto))] -[JsonSerializable(typeof(List<UserSessionDto>))] public partial class AppJsonContext : JsonSerializerContext { } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs index 91b296fa8d..974370208e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs @@ -14,4 +14,6 @@ public partial class CategoryDto public string? Color { get; set; } = "#FFFFFF"; public int ProductsCount { get; set; } + + public byte[] ConcurrencyStamp { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/OverallAnalyticsStatsDataResponseDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/OverallAnalyticsStatsDataResponseDto.cs index c130f412e9..bfac0a33ee 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/OverallAnalyticsStatsDataResponseDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/OverallAnalyticsStatsDataResponseDto.cs @@ -2,11 +2,11 @@ public partial class OverallAnalyticsStatsDataResponseDto { - public int Last30DaysProductCount { get; set; } - - public int Last30DaysCategoryCount { get; set; } + public int TotalCategories { get; set; } public int TotalProducts { get; set; } - public int TotalCategories { get; set; } + public int CategoriesWithProductCount { get; set; } + + public int Last30DaysProductCount { get; set; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/ProductSaleStatResponseDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/ProductSaleStatResponseDto.cs deleted file mode 100644 index 9b69235541..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Dashboard/ProductSaleStatResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Boilerplate.Shared.Dtos.Dashboard; - -public partial class ProductSaleStatResponseDto -{ - public string? ProductName { get; set; } - - public string? CategoryColor { get; set; } - - public decimal SaleAmount { get; set; } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/ChangePasswordRequestDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/ChangePasswordRequestDto.cs index b13f2156b3..18869778ca 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/ChangePasswordRequestDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/ChangePasswordRequestDto.cs @@ -8,7 +8,6 @@ public partial class ChangePasswordRequestDto [Display(Name = nameof(AppStrings.OldPassword))] public string? OldPassword { get; set; } - [Required(ErrorMessage = nameof(AppStrings.RequiredAttribute_ValidationError))] [MinLength(6, ErrorMessage = nameof(AppStrings.MinLengthAttribute_ValidationError))] [Display(Name = nameof(AppStrings.NewPassword))] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/EditUserDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/EditUserDto.cs index cfc8b01468..de889f66e3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/EditUserDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/EditUserDto.cs @@ -8,24 +8,8 @@ public partial class EditUserDto public string? FullName { get; set; } [Display(Name = nameof(AppStrings.Gender))] - public Gender? Gender { get; set; } + public Gender Gender { get; set; } [Display(Name = nameof(AppStrings.BirthDate))] public DateTimeOffset? BirthDate { get; set; } - - [JsonIgnore] - public string? GenderAsString - { - get - { - return Gender?.ToString(); - } - set - { - if (string.IsNullOrEmpty(value) is false) - { - Gender = Enum.Parse<Gender>(value); - } - } - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/IdentityRequestDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/IdentityRequestDto.cs index 961b1c0c8a..6a5ca0e0cd 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/IdentityRequestDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/IdentityRequestDto.cs @@ -21,6 +21,11 @@ public partial class IdentityRequestDto : IValidatableObject public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (string.IsNullOrEmpty(UserName) && string.IsNullOrEmpty(Email) && string.IsNullOrEmpty(PhoneNumber)) - yield return new ValidationResult(errorMessage: nameof(AppStrings.EitherProvideUserNameOrEmailOrPhoneNumber), [nameof(Email), nameof(PhoneNumber)]); + { + yield return new ValidationResult( + errorMessage: nameof(AppStrings.EitherProvideUserNameOrEmailOrPhoneNumber), + memberNames: [nameof(UserName), nameof(Email), nameof(PhoneNumber)] + ); + } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignInRequestDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignInRequestDto.cs index 48f1d816e0..1ff059fd61 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignInRequestDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignInRequestDto.cs @@ -13,6 +13,7 @@ public partial class SignInRequestDto : IdentityRequestDto /// For either Otp or magic link /// </summary> /// <example>null</example> + [StringLength(6)] [Display(Name = nameof(AppStrings.Otp))] public string? Otp { get; set; } @@ -27,17 +28,6 @@ public partial class SignInRequestDto : IdentityRequestDto [Display(Name = nameof(AppStrings.TwoFactorCode))] public string? TwoFactorCode { get; set; } - /// <summary> - /// Two factor token received by email or sms - /// </summary> - /// <example>null</example> - [Display(Name = nameof(AppStrings.TwoFactorToken))] - public string? TwoFactorToken { get; set; } - - /// <example>null</example> - [Display(Name = nameof(AppStrings.TwoFactorRecoveryCode))] - public string? TwoFactorRecoveryCode { get; set; } - /// <example>Samsung Android 14</example> public string? DeviceInfo { get; set; } @@ -47,7 +37,10 @@ public override IEnumerable<ValidationResult> Validate(ValidationContext validat if (string.IsNullOrEmpty(Password) && string.IsNullOrEmpty(Otp)) { - result.Add(new ValidationResult(errorMessage: nameof(AppStrings.EitherProvidePasswordOrOtp), [nameof(Password), nameof(Otp)])); + result.Add(new ValidationResult( + errorMessage: nameof(AppStrings.EitherProvidePasswordOrOtp), + memberNames: [nameof(Password), nameof(Otp)] + )); } return result; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignUpRequestDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignUpRequestDto.cs index bd4ad95323..2e80ee5827 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignUpRequestDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/SignUpRequestDto.cs @@ -15,11 +15,6 @@ public partial class SignUpRequestDto : IdentityRequestDto [Display(Name = nameof(AppStrings.Password))] public string? Password { get; set; } - /// <example>true</example> - //[Range(typeof(bool), "true", "true", ErrorMessage = nameof(AppStrings.YouHaveToAcceptTerms))] - //[Display(Name = nameof(AppStrings.TermsAccepted))] - //public bool TermsAccepted { get; set; } - //#if (captcha == "reCaptcha") /// <example>null</example> public string? GoogleRecaptchaResponse { get; set; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs index aef0fd6457..b19cfc4f7c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs @@ -38,6 +38,9 @@ public partial class UserDto : IValidatableObject public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (string.IsNullOrEmpty(Email) && string.IsNullOrEmpty(PhoneNumber)) - yield return new ValidationResult(errorMessage: nameof(AppStrings.EitherProvideEmailOrPhoneNumber), [nameof(Email), nameof(PhoneNumber)]); + yield return new ValidationResult( + errorMessage: nameof(AppStrings.EitherProvideEmailOrPhoneNumber), + memberNames: [nameof(Email), nameof(PhoneNumber)] + ); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs index cb7b7c6d67..d2bb317b9a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs @@ -10,7 +10,7 @@ public partial class UserSessionDto public string? Address { get; set; } - public string? LastSeenOn { get; set; } + public DateTimeOffset RenewedOn { get; set; } public bool IsValid { get; set; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/IdentityJsonContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/IdentityJsonContext.cs new file mode 100644 index 0000000000..806b9411e9 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/IdentityJsonContext.cs @@ -0,0 +1,31 @@ +//+:cnd:noEmit +using Boilerplate.Shared.Dtos.Identity; + +namespace Boilerplate.Shared.Dtos; + +/// <summary> +/// https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/ +/// </summary> +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] +[JsonSerializable(typeof(UserDto))] +[JsonSerializable(typeof(IdentityRequestDto))] +[JsonSerializable(typeof(SignInRequestDto))] +[JsonSerializable(typeof(SignInResponseDto))] +[JsonSerializable(typeof(TokenResponseDto))] +[JsonSerializable(typeof(RefreshRequestDto))] +[JsonSerializable(typeof(SignUpRequestDto))] +[JsonSerializable(typeof(EditUserDto))] +[JsonSerializable(typeof(SendEmailTokenRequestDto))] +[JsonSerializable(typeof(SendPhoneTokenRequestDto))] +[JsonSerializable(typeof(ConfirmEmailRequestDto))] +[JsonSerializable(typeof(ChangeEmailRequestDto))] +[JsonSerializable(typeof(ConfirmPhoneRequestDto))] +[JsonSerializable(typeof(ChangePhoneNumberRequestDto))] +[JsonSerializable(typeof(SendResetPasswordTokenRequestDto))] +[JsonSerializable(typeof(ResetPasswordRequestDto))] +[JsonSerializable(typeof(TwoFactorAuthRequestDto))] +[JsonSerializable(typeof(TwoFactorAuthResponseDto))] +[JsonSerializable(typeof(List<UserSessionDto>))] +public partial class IdentityJsonContext : JsonSerializerContext +{ +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs index c7ef12a4d2..94b3aab110 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs @@ -25,4 +25,6 @@ public partial class ProductDto [Display(Name = nameof(AppStrings.Category))] public string? CategoryName { get; set; } + + public byte[] ConcurrencyStamp { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PushNotification/DeviceInstallationDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PushNotification/DeviceInstallationDto.cs new file mode 100644 index 0000000000..d681913ed2 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PushNotification/DeviceInstallationDto.cs @@ -0,0 +1,18 @@ +namespace Boilerplate.Shared.Dtos.PushNotification; + +[DtoResourceType(typeof(AppStrings))] +public partial class DeviceInstallationDto +{ + [Required(ErrorMessage = nameof(AppStrings.RequiredAttribute_ValidationError))] + public string? InstallationId { get; set; } + + [Required(ErrorMessage = nameof(AppStrings.RequiredAttribute_ValidationError))] + [AllowedValues("apns", "fcmV1", "browser")] + /// <example>fcmV1</example> + public string? Platform { get; set; } + + public string? PushChannel { get; set; } + public string? P256dh { get; set; } + public string? Auth { get; set; } + public string? Endpoint { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Enums/Gender.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Enums/Gender.cs index 8f43dcb1bb..92016faf6b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Enums/Gender.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Enums/Gender.cs @@ -3,7 +3,7 @@ [JsonConverter(typeof(JsonStringEnumConverter<Gender>))] public enum Gender { + Other, Male, Female, - Other } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs index 130b4c2069..993787b4a4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs @@ -27,8 +27,13 @@ public static string GetDisplayName(this ClaimsPrincipal claimsPrincipal) return claimsPrincipal.GetEmail() ?? claimsPrincipal.GetUserName(); } - public static string? GetSessionId(this ClaimsPrincipal claimsPrincipal) + /// <summary> + /// Returns the user session id stored in sessions column of user table after user sign in. + /// </summary> + public static Guid? GetSessionId(this ClaimsPrincipal claimsPrincipal) { - return claimsPrincipal.FindFirst("session-id")?.Value; + return claimsPrincipal.IsAuthenticated() + ? Guid.Parse(claimsPrincipal.FindFirst("session-id")!.Value) + : null; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ICollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ICollectionExtensions.cs index ca12ef1795..841ae86d21 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ICollectionExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ICollectionExtensions.cs @@ -11,4 +11,9 @@ public static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items, } return results; } + + public static IEnumerable<(T item, int index)> Indexed<T>(this IEnumerable<T> source) + { + return source.Select((item, index) => (item, index)); + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationBuilderExtensions.cs index 2c40c40e32..4949ef4661 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationBuilderExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IConfigurationBuilderExtensions.cs @@ -7,10 +7,10 @@ public static partial class IConfigurationBuilderExtensions /// <summary> /// Configuration priority (Lowest to highest) => /// Shared/appsettings.json - /// Shared/appsettings.Production.json + /// Shared/appsettings.{environment}.json (If present) /// Server.Api only => /// Server/appsettings.json - /// Server/appsettings.Production.json + /// Server/appsettings.{environment}.json (If present) /// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration#default-application-configuration-sources /// </summary> public static void AddSharedConfigurations(this IConfigurationBuilder builder) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ISharedServiceCollectionExtensions.cs similarity index 53% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IServiceCollectionExtensions.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ISharedServiceCollectionExtensions.cs index 88d79c75a0..f9ec763658 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/IServiceCollectionExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ISharedServiceCollectionExtensions.cs @@ -1,15 +1,32 @@ -using Microsoft.AspNetCore.Components.Web; +using Boilerplate.Shared; +using Microsoft.AspNetCore.Components.Web; namespace Microsoft.Extensions.DependencyInjection; -public static partial class IServiceCollectionExtensions +public static partial class ISharedServiceCollectionExtensions { - public static IServiceCollection AddSharedProjectServices(this IServiceCollection services) + public static IServiceCollection AddSharedProjectServices(this IServiceCollection services, IConfiguration configuration) { // Services being registered here can get injected everywhere (Api, Web, Android, iOS, Windows and macOS) - services.TryAddTransient<IDateTimeProvider, DateTimeProvider>(); - services.TryAddTransient<CultureInfoManager>(); + services.AddScoped<HtmlRenderer>(); + services.AddScoped<CultureInfoManager>(); + services.AddScoped<IDateTimeProvider, DateTimeProvider>(); + + services.AddSingleton(sp => configuration.Get<SharedSettings>()!); + services.AddSingleton(sp => + { + JsonSerializerOptions options = new JsonSerializerOptions(AppJsonContext.Default.Options); + + options.TypeInfoResolverChain.Add(IdentityJsonContext.Default); + + return options; + }); + + services.AddOptions<SharedSettings>() + .Bind(configuration) + .ValidateDataAnnotations() + .ValidateOnStart(); // Define authorization policies here to seamlessly integrate them across various components, // including web api actions and razor pages using Authorize attribute, AuthorizeView in razor pages, @@ -22,10 +39,6 @@ public static IServiceCollection AddSharedProjectServices(this IServiceCollectio services.AddLocalization(); - services.TryAddTransient(typeof(Lazy<>), typeof(Lazy<>)); // add support for lazy injection - services.TryAddTransient<HtmlRenderer>(); - services.TryAddTransient(sp => AppJsonContext.Default.Options); - return services; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/JsonSeralizerExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/JsonSeralizerExtensions.cs index 3f5745420d..386d722246 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/JsonSeralizerExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/JsonSeralizerExtensions.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization.Metadata; +using System.Collections.Concurrent; +using System.Text.Json.Serialization.Metadata; namespace System.Text.Json; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/LinqExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/LinqExtensions.cs index 4941350b3a..fee57093a2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/LinqExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/LinqExtensions.cs @@ -4,36 +4,55 @@ namespace System.Collections.Generic; public static partial class LinqExtensions { - /// <summary> - /// https://extensionmethod.net/csharp/ienumerable-t/whereif - /// </summary> + /// <summary> + /// </summary> public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool predicate, Expression<Func<T, bool>> itemPredicate) { return predicate ? query.Where(itemPredicate) : query; } - public static IQueryable<T> OrderByIf<T>(this IQueryable<T> query, bool predicate, Expression<Func<T, object>> keySelector) + public static IQueryable<T> OrderByIf<T, TKey>(this IQueryable<T> query, bool predicate, Expression<Func<T, TKey>> keySelector) { return predicate ? query.OrderBy(keySelector) : query; } - public static IQueryable<T> OrderByDescendingIf<T>(this IQueryable<T> query, bool predicate, Expression<Func<T, object>> keySelector) + public static IQueryable<T> OrderByDescendingIf<T, TKey>(this IQueryable<T> query, bool predicate, Expression<Func<T, TKey>> keySelector) { return predicate ? query.OrderByDescending(keySelector) : query; } + public static IQueryable<T> SkipIf<T>(this IQueryable<T> query, bool predicate, int count) + { + return predicate ? query.Skip(count) : query; + } + + public static IQueryable<T> TakeIf<T>(this IQueryable<T> query, bool predicate, int count) + { + return predicate ? query.Take(count) : query; + } + public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool predicate, Func<T, bool> itemPredicate) { return predicate ? source.Where(itemPredicate) : source; } - public static IEnumerable<T> OrderByIf<T>(this IEnumerable<T> source, bool predicate, Func<T, object> keySelector) + public static IEnumerable<T> OrderByIf<T, TKey>(this IEnumerable<T> source, bool predicate, Func<T, TKey> keySelector) { return predicate ? source.OrderBy(keySelector) : source; } - public static IEnumerable<T> OrderByDescendingIf<T>(this IEnumerable<T> source, bool predicate, Func<T, object> keySelector) + public static IEnumerable<T> OrderByDescendingIf<T, TKey>(this IEnumerable<T> source, bool predicate, Func<T, TKey> keySelector) { return predicate ? source.OrderByDescending(keySelector) : source; } + + public static IEnumerable<T> SkipIf<T>(this IEnumerable<T> source, bool predicate, int count) + { + return predicate ? source.Skip(count) : source; + } + + public static IEnumerable<T> TakeIf<T>(this IEnumerable<T> source, bool predicate, int count) + { + return predicate ? source.Take(count) : source; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/UriExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/UriExtensions.cs new file mode 100644 index 0000000000..a24971c451 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/UriExtensions.cs @@ -0,0 +1,67 @@ +using System.Web; + +namespace System; + +public static partial class UriExtensions +{ + public static string GetUrlWithoutQueryParameter(this Uri uri, string key) + { + var newQueryString = HttpUtility.ParseQueryString(uri.Query); + newQueryString.Remove(key); + + string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path); + + return newQueryString.Count > 0 + ? $"{pagePathWithoutQueryString}?{newQueryString}" + : pagePathWithoutQueryString; + } + + /// <summary> + /// Reads culture from either route segment or query string. + /// https://adminpanel.bitpaltform.dev/en-US/categories + /// https://adminpanel.bitpaltform.dev/categories?culture=en-US + /// </summary> + public static string? GetCulture(this Uri uri) + { + if (CultureInfoManager.MultilingualEnabled is false) + return null; + + var culture = HttpUtility.ParseQueryString(uri.Query)["culture"]; + + if (string.IsNullOrEmpty(culture) is false) + return culture; + + foreach (var segment in uri.Segments.Take(2)) + { + var segmentValue = segment.Trim('/'); + if (CultureInfoManager.SupportedCultures.Any(sc => string.Equals(sc.Culture.Name, segmentValue, StringComparison.InvariantCultureIgnoreCase))) + { + return segmentValue; + } + } + + return null; + } + + public static string GetUrlWithoutCulture(this Uri uri) + { + uri = new Uri(uri.GetUrlWithoutQueryParameter("culture")); + + var culture = uri.GetCulture(); + + if (string.IsNullOrEmpty(culture) is false) + { + uri = new Uri(uri.ToString() + .Replace($"{culture}/", string.Empty) + .Replace(culture, string.Empty)); + } + + return uri.ToString(); + } + + public static string GetPath(this Uri uri) + { + var uriBuilder = new UriBuilder(uri.GetUrlWithoutCulture()) { Query = string.Empty, Fragment = string.Empty }; + return uriBuilder.Path; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx index 3ea89ee495..84e99323b0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx @@ -146,6 +146,9 @@ </data> <data name="ConflictException" xml:space="preserve"> <value>درخواست به دلیل مغایرت داشتن پردازش نشد</value> + </data> + <data name="NotAuthorizedPageTitle" xml:space="preserve"> + <value>403</value> </data> <data name="ForbiddenException" xml:space="preserve"> <value>دسترسی به منابع درخواست شده ممنوع است</value> @@ -182,16 +185,40 @@ <value>همه</value> </data> <data name="Alphabetical" xml:space="preserve"> - <value>الفبایی</value> + <value>ا-ی</value> </data> <data name="Completed" xml:space="preserve"> <value>تمام شده</value> </data> <data name="Date" xml:space="preserve"> <value>تاریخ</value> + </data> + <data name="SettingsPageTitle" xml:space="preserve"> + <value>تنظیمات</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>تنظیمات</value> + </data> + <data name="Dark" xml:space="preserve"> + <value>تیره</value> + </data> + <data name="Light" xml:space="preserve"> + <value>روشن</value> + </data> + <data name="Language" xml:space="preserve"> + <value>زبان</value> + </data> + <data name="SelectLanguage" xml:space="preserve"> + <value>انتخاب زبان</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>تنظیمات</value> </data> <data name="ProfileTitle" xml:space="preserve"> <value>پروفایل</value> + </data> + <data name="ProfileSubtitle" xml:space="preserve"> + <value>داده های پروفایل خود را تغییر دهید</value> </data> <data name="EditProfileTitle" xml:space="preserve"> <value>ویرایش پروفایل</value> @@ -207,6 +234,15 @@ </data> <data name="SuccessfulSendChangePhoneNumberTokenMessage" xml:space="preserve"> <value>توکن تغییر شماره تلفن برای شما ارسال شد</value> + </data> + <data name="SuccessfulSendTokenMessage" xml:space="preserve"> + <value>توکن برای شما ارسال شد</value> + </data> + <data name="SessionsTitle" xml:space="preserve"> + <value>جلسه ها</value> + </data> + <data name="SessionsSubtitle" xml:space="preserve"> + <value>جلسه های فعال شما در دستگاههای مختلف</value> </data> <data name="UserSessionsTitle" xml:space="preserve"> <value>سشنهای شما</value> @@ -222,27 +258,34 @@ </data> <data name="RemoveSessionSuccessMessage" xml:space="preserve"> <value>حذف سشن با موفقیت انجام شد</value> + </data> + <data name="NotFoundPageTitle" xml:space="preserve"> + <value>404</value> </data> <data name="NotFoundText" xml:space="preserve"> <value>چیزی پیدا نشد.</value> </data> - <data name="UnknwonDevice" xml:space="preserve"> + <data name="UnknownDevice" xml:space="preserve"> <value>دستگاه نامشخص</value> + </data> + <data name="UpdateWebViewThroughGooglePlay" xml:space="preserve"> + <value>لطفا وب ویو را از طریق Google Play آپدیت کنید</value> + </data> + <data name="Ok" xml:space="preserve"> + <value>باشه</value> </data> <!--#if (offlineDb == true) --> <data name="OfflineEditProfileTitle" xml:space="preserve"> <value>ویرایش پروفایل آفلاین</value> - </data> - <!--#endif--> - <!--#if (signalr == true) --> - <data name="TwoFactorTokenPushText" xml:space="preserve"> - <value>{0} توکن احراز هویت مرحله دو شما است در Boilerplate.</value> </data> <!--#endif--> <data name="FullName" xml:space="preserve"> <value>نام کامل</value> </data> - <data name="TermsTitle" xml:space="preserve"> + <data name="TermsPageTitle" xml:space="preserve"> + <value>شرایط</value> + </data> + <data name="Terms" xml:space="preserve"> <value>شرایط</value> </data> <data name="ProfileUpdatedSuccessfullyMessage" xml:space="preserve"> @@ -265,6 +308,9 @@ </data> <data name="NewEmailPlaceholder" xml:space="preserve"> <value>ایمیل جدید را وارد کنید</value> + </data> + <data name="Phone" xml:space="preserve"> + <value>تلفن</value> </data> <data name="PhoneNumber" xml:space="preserve"> <value>شماره تلفن</value> @@ -301,9 +347,6 @@ </data> <data name="Error" xml:space="preserve"> <value>خطا</value> - </data> - <data name="TermsAccepted" xml:space="preserve"> - <value>آیا شرایط را قبول دارید؟</value> </data> <data name="YouHaveToAcceptTerms" xml:space="preserve"> <value>شما باید با شرایط ما موافقت کنید.</value> @@ -355,6 +398,18 @@ </data> <data name="Home" xml:space="preserve"> <value>خانه</value> + </data> + <data name="Refresh" xml:space="preserve"> + <value>تلاش مجدد</value> + </data> + <data name="Recover" xml:space="preserve"> + <value>بازیابی</value> + </data> + <data name="SomethingWentWrong" xml:space="preserve"> + <value>اوه! به نظر مشکلی پیش اومده...</value> + </data> + <data name="BackToHome" xml:space="preserve"> + <value>بازگشت به خانه</value> </data> <data name="UserLockedOut" xml:space="preserve"> <value>کاربر قفل شده است. دوباره امتحان کنید در {0}</value> @@ -397,6 +452,9 @@ </data> <data name="ConfirmEmailSubtitle" xml:space="preserve"> <value>ما یک توکن به آدرس ایمیل شما ارسال کرده ایم.</value> + </data> + <data name="ConfirmPageTitle" xml:space="preserve"> + <value>تایید</value> </data> <data name="ConfirmTitle" xml:space="preserve"> <value>تایید حساب کاربری</value> @@ -443,7 +501,7 @@ <data name="PhoneTokenConfirmButtonText" xml:space="preserve"> <value>تایید شماره تلفن</value> </data> - <data name="NotReceivedConfirmationPhoneMessage" xml:space="preserve"> + <data name="NotReceivedPhoneMessage" xml:space="preserve"> <value>آیا توکن تلفن خود را دریافت نکرده اید؟</value> </data> <data name="ResendConfirmationPhoneTokenMessage" xml:space="preserve"> @@ -457,6 +515,12 @@ </data> <data name="PhoneConfirmationSuccessMessage" xml:space="preserve"> <value>اکنون می توانید با حساب کاربری خود وارد شوید.</value> + </data> + <data name="AccountTitle" xml:space="preserve"> + <value>حساب کاربری</value> + </data> + <data name="AccountSubtitle" xml:space="preserve"> + <value>ایمیل یا شماره تلفن حساب کاربری خود را تغییر دهید</value> </data> <data name="DeleteAccount" xml:space="preserve"> <value>حذف حساب</value> @@ -482,7 +546,7 @@ <data name="FileUploadFailed" xml:space="preserve"> <value>هنگام بارگذاری فایل خطایی رخ داد</value> </data> - <data name="ForgotPassword" xml:space="preserve"> + <data name="ForgotPasswordPageTitle" xml:space="preserve"> <value>فراموشی رمزعبور</value> </data> <data name="ForgotPasswordTitle" xml:space="preserve"> @@ -509,8 +573,29 @@ <data name="DescriptionMetaTagValue" xml:space="preserve"> <value>پروژه Boilerplate با ASP.NET Core, Identity, Web API, EF Core و Blazor ساخته شده</value> </data> - <data name="HomeTitle" xml:space="preserve"> - <value>صفحه اصلی Boilerplate</value> + <data name="HomePageTitle" xml:space="preserve"> + <value>خانه</value> + </data> + <data name="HomePanelTitle" xml:space="preserve"> + <value>تمام اپهای خود را بسازید</value> + </data> + <data name="HomePanelSubtitle" xml:space="preserve"> + <value>با استفاده چیزی که میدانید و دوستش دارید</value> + </data> + <data name="WatchVideo" xml:space="preserve"> + <value>مشاهده ویدئو</value> + </data> + <data name="LearnMore" xml:space="preserve"> + <value>بیشتر بدانید</value> + </data> + <data name="BitPlatformMessage" xml:space="preserve"> + <value>ابزارهایی برای دولوپرهای دات نت در پلتفرهای گوناگون</value> + </data> + <data name="BitBlazorUIMessage" xml:space="preserve"> + <value>مجموعه ای از کمپوننتهای کارا، ساده و زیبای بلیزری</value> + </data> + <data name="BitProjectTemplateMessage" xml:space="preserve"> + <value>تمپلیت پروژه ای فول امکانات که همه جا کار میکند</value> </data> <data name="InvalidConfirmationTokenMessage" xml:space="preserve"> <value>به نظر می رسد لینک تایید نامعتبر است یا منقضی شده است.</value> @@ -524,8 +609,8 @@ <data name="No" xml:space="preserve"> <value>نه</value> </data> - <data name="NotReceivedConfirmationEmailMessage" xml:space="preserve"> - <value>ایمیل تایید را دریافت نکرده اید؟</value> + <data name="NotReceivedEmailMessage" xml:space="preserve"> + <value>ایمیلی دریافت نکرده اید؟</value> </data> <data name="Or" xml:space="preserve"> <value>یا</value> @@ -538,6 +623,12 @@ </data> <data name="ResendEmailTokenButtonText" xml:space="preserve"> <value>ارسال دوباره ایمیل</value> + </data> + <data name="ResetPasswordPageTitle" xml:space="preserve"> + <value>ریست رمز عبور</value> + </data> + <data name="ResetPassword" xml:space="preserve"> + <value>ریست رمز عبور</value> </data> <data name="ResetPasswordTitle" xml:space="preserve"> <value>ریست رمز عبور</value> @@ -563,8 +654,17 @@ <data name="Save" xml:space="preserve"> <value>ذخیره</value> </data> - <data name="SignInTitle" xml:space="preserve"> + <data name="SignInPageTitle" xml:space="preserve"> <value>ورود به برنامه</value> + </data> + <data name="SignInPanelTitle" xml:space="preserve"> + <value>خوش آمدید</value> + </data> + <data name="SignInPanelSubtitle" xml:space="preserve"> + <value>وارد حساب کاربری خود شوید با</value> + </data> + <data name="Continue" xml:space="preserve"> + <value>ادامه</value> </data> <data name="SocialSignedInTitle" xml:space="preserve"> <value>ورود سوشال</value> @@ -602,8 +702,14 @@ <data name="SignUp" xml:space="preserve"> <value>ثبت نام</value> </data> - <data name="SingUpTitle" xml:space="preserve"> + <data name="SignUpPageTitle" xml:space="preserve"> <value>ثبت نام</value> + </data> + <data name="SignUpPanelTitle" xml:space="preserve"> + <value>امروز آغاز کنید</value> + </data> + <data name="SignUpPanelSubtitle" xml:space="preserve"> + <value>یک اکانت جدید بسازید با</value> </data> <data name="Submit" xml:space="preserve"> <value>ارسال</value> @@ -619,9 +725,6 @@ </data> <data name="SelectBirthDate" xml:space="preserve"> <value>تاریخ تولد خود را انتخاب کنید</value> - </data> - <data name="ResetPassword" xml:space="preserve"> - <value>ریست رمز عبور</value> </data> <data name="Action" xml:space="preserve"> <value>عملیات</value> @@ -667,12 +770,42 @@ </data> <data name="RememberMe" xml:space="preserve"> <value>من رو به خاطر بسپار؟</value> + </data> + <data name="Copy" xml:space="preserve"> + <value>کپی</value> + </data> + <data name="Copied" xml:space="preserve"> + <value>کپی شد</value> + </data> + <data name="TfaTitle" xml:space="preserve"> + <value>احراز هویت دو عاملی</value> + </data> + <data name="TfaSubtitle" xml:space="preserve"> + <value>احراز هویت چند عاملی حساب کاربری خود را مدیریت کنید</value> </data> <data name="TwoFactorCode" xml:space="preserve"> - <value>کد احراز هویت</value> + <value>کد 2-عاملی</value> </data> <data name="TwoFactorRecoveryCode" xml:space="preserve"> <value>کد بازیابی</value> + </data> + <data name="TfaPanelTitle" xml:space="preserve"> + <value>احراز هویت 2-عاملی</value> + </data> + <data name="TfaPanelSubtitle" xml:space="preserve"> + <value>یک کد جدید از برنامه احراز هویت خود دریافت کنید یا از کد بازیابی خود استفاده کنید.</value> + </data> + <data name="TfaPanelAnotherWayTitle" xml:space="preserve"> + <value>راه دیگری را آزمایش کنید</value> + </data> + <data name="TfaPanelAnotherWaySubtitle" xml:space="preserve"> + <value>شما میتوانید یک کد جدید در ایمیل یا تلفن خود دریافت کنید.</value> + </data> + <data name="TfaPanelAnotherWayGetCode" xml:space="preserve"> + <value>Get code</value> + </data> + <data name="TwoFactorAuthTitle" xml:space="preserve"> + <value>2FA</value> </data> <data name="TwoFactorAuthTitle" xml:space="preserve"> <value>2FA</value> @@ -723,7 +856,7 @@ <value>تأیید</value> </data> <data name="TfaRecoveryCodesHeader" xml:space="preserve"> - <value>کدهای بازیابی</value> + <value>بازیابی</value> </data> <data name="TfaRecoveryCodesZeroLeftTitle" xml:space="preserve"> <value>هیچ کد بازیابی ندارید.</value> @@ -759,7 +892,7 @@ <value>کدهای بازیابی:</value> </data> <data name="TfaAuthAppHeader" xml:space="preserve"> - <value>برنامه Authenticator</value> + <value>برنامه</value> </data> <data name="TfaAuthAppWarningTitle" xml:space="preserve"> <value>اگر کلید احراز هویت خود را بازنشانی کنید، برنامه احراز هویت شما تا زمانی که آن را دوباره پیکربندی نکنید، کار نخواهد کرد.</value> @@ -771,7 +904,7 @@ <value>کلید احراز هویت را بازنشانی کنید</value> </data> <data name="TfaDisable2faHeader" xml:space="preserve"> - <value>2FA را غیرفعال کنید</value> + <value>غیرفعالسازی</value> </data> <data name="TfaDisable2faWarningTitle" xml:space="preserve"> <value>این عمل فقط 2FA را غیرفعال می کند.</value> @@ -808,6 +941,21 @@ </data> <data name="Otp" xml:space="preserve"> <value>رمز یکبار مصرف</value> + </data> + <data name="OtpPanelTitle" xml:space="preserve"> + <value>{0} خود را بررسی کنید</value> + </data> + <data name="OtpPanelSubtitle" xml:space="preserve"> + <value>{0} خود را با ورود کد ارسال شده به {1} تایید کنید</value> + </data> + <data name="Code" xml:space="preserve"> + <value>کد</value> + </data> + <data name="OtpResendMessage" xml:space="preserve"> + <value>آیا کدی دریافت نکردید?</value> + </data> + <data name="Resend" xml:space="preserve"> + <value>ارسال دوباره</value> </data> <data name="OtpPlaceholder" xml:space="preserve"> <value>رمز یکبار مصرف را وارد کنید</value> @@ -833,13 +981,13 @@ <data name="ConfirmPhoneTokenSmsText" xml:space="preserve"> <value>{0} توکن تایید شماره تلفن شما است در Boilerplate.</value> </data> - <data name="TwoFactorTokenSmsText" xml:space="preserve"> + <data name="TwoFactorTokenShortText" xml:space="preserve"> <value>{0} توکن احراز هویت مرحله دو شما است در Boilerplate.</value> </data> - <data name="OtpSmsText" xml:space="preserve"> + <data name="OtpShortText" xml:space="preserve"> <value>{0} پسورد یکبار مصرف شما است در Boilerplate.</value> </data> - <data name="ResetPasswordTokenSmsText" xml:space="preserve"> + <data name="ResetPasswordTokenShortText" xml:space="preserve"> <value>{0} توکن تغییر رمز عبور شما است در Boilerplate.</value> </data> <data name="Online" xml:space="preserve"> @@ -862,6 +1010,9 @@ </data> <data name="AboutTitle" xml:space="preserve"> <value>درباره</value> + </data> + <data name="NothingFound" xml:space="preserve"> + <value>چیزی پیدا نشد!</value> </data> <!--#if (sample == "Todo") --> <data name="TodoAddPlaceholder" xml:space="preserve"> @@ -871,18 +1022,24 @@ <value>هنوز کاری ثبت نشده</value> </data> <data name="TodoSearchPlaceholder" xml:space="preserve"> - <value>جستجو در کارها...</value> + <value>جستجوی کارها...</value> </data> <data name="ToDoItemCouldNotBeFound" xml:space="preserve"> <value>کار پیدا نشد</value> </data> - <data name="TodoTitle" xml:space="preserve"> + <data name="TodoPageTitle" xml:space="preserve"> + <value>کار</value> + </data> + <data name="Todo" xml:space="preserve"> <value>کار</value> </data> <data name="DeleteTodoItem" xml:space="preserve"> <value>حذف کار</value> </data> <!--#elif (sample == "Admin") --> + <data name="AdminPanel" xml:space="preserve"> + <value>پنل ادمین</value> + </data> <data name="ProductsCount" xml:space="preserve"> <value>تعداد محصولات</value> </data> @@ -916,8 +1073,8 @@ <data name="EnterProductName" xml:space="preserve"> <value>نام محصول را وارد کنید</value> </data> - <data name="Last30DaysCategoryCount" xml:space="preserve"> - <value>تعداد دسته های ۳۰ روز گذشته</value> + <data name="CategoriesWithProductCount" xml:space="preserve"> + <value>تعداد دسته های دارای محصول</value> </data> <data name="ProductSales" xml:space="preserve"> <value>فروش محصولات</value> @@ -981,9 +1138,15 @@ </data> <data name="EditProduct" xml:space="preserve"> <value>ویرایش محصول</value> + </data> + <data name="DashboardPageTitle" xml:space="preserve"> + <value>داشبورد</value> </data> <data name="Dashboard" xml:space="preserve"> <value>داشبورد</value> + </data> + <data name="DashboardSubtitle" xml:space="preserve"> + <value>داده های تحلیلی شما</value> </data> <!--#if (captcha == "reCaptcha") --> <data name="InvalidGoogleRecaptchaResponse" xml:space="preserve"> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fr.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx similarity index 53% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fr.resx rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx index 7282eacc82..3873a39d58 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fr.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx @@ -1,18 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> <!-- +:cnd:noEmit --> <root> - <!-- - Microsoft ResX Schema - + <!-- + Microsoft ResX Schema + Version 2.0 - - The primary goals of this format is to allow a simple XML format - that is mostly human readable. The generation and parsing of the - various data types are done through the TypeConverter classes + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes associated with the data types. - + Example: - + ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> @@ -27,36 +27,36 @@ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> - - There are any number of "resheader" rows that contain simple + + There are any number of "resheader" rows that contain simple name/value pairs. - - Each data row contains a name, and value. The row also contains a - type or mimetype. Type corresponds to a .NET class that support - text/value conversion through the TypeConverter architecture. - Classes that don't support this are serialized and stored with the + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the mimetype set. - - The mimetype is used for serialized objects, and tells the - ResXResourceReader how to depersist the object. This is currently not + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: - - Note - application/x-microsoft.net.object.binary.base64 is the format - that the ResXResourceWriter will generate, however the reader can + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. - + mimetype: application/x-microsoft.net.object.binary.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. - + mimetype: application/x-microsoft.net.object.soap.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array + value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> @@ -120,877 +120,1034 @@ </resheader> <!-- .NET Validation messages --> <data name="CompareAttribute_ValidationError" xml:space="preserve"> - <value>« {0} » et « {1} » ne correspondent pas.</value> + <value>'{0}' en '{1}' komen niet overeen.</value> </data> <data name="EmailAddressAttribute_ValidationError" xml:space="preserve"> - <value>Le champ {0} n'est pas une adresse e-mail valide.</value> + <value>Het veld {0} is geen geldig e-mailadres.</value> </data> <data name="MaxLengthAttribute_InvalidMaxLength" xml:space="preserve"> - <value>MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.</value> + <value>MaxLengthAttribute moet een Length-waarde hebben die groter is dan nul. Gebruik MaxLength() zonder parameters om aan te geven dat de tekenreeks of matrix de maximaal toegestane lengte kan hebben.</value> </data> <data name="MinLengthAttribute_ValidationError" xml:space="preserve"> - <value>Le champ {0} doit être de type chaîne ou tableau avec une longueur minimale de « {1} ».</value> + <value>Het veld {0} moet een tekenreeks- of matrixtype zijn met een minimale lengte van '{1}'.</value> </data> <data name="RangeAttribute_ValidationError" xml:space="preserve"> - <value>Le champ {0} doit être compris entre {1} et {2}.</value> + <value>Het veld {0} moet tussen {1} en {2} liggen.</value> </data> <data name="RequiredAttribute_ValidationError" xml:space="preserve"> - <value>Le champ {0} est obligatoire.</value> + <value>Het veld {0} is verplicht.</value> </data> <data name="PhoneAttribute_ValidationError" xml:space="preserve"> - <value>Le champ {0} n'est pas un numéro de téléphone valide.</value> + <value>Het veld {0} is geen geldig telefoonnummer.</value> </data> <!-- Exceptions --> <data name="BadRequestException" xml:space="preserve"> - <value>Requête invalide</value> + <value>Ongeldig verzoek</value> </data> <data name="ConflictException" xml:space="preserve"> - <value>La demande n'a pas pu être traitée en raison d'un conflit dans la demande</value> + <value>Aanvraag kan niet worden verwerkt vanwege een conflict in de aanvraag</value> + </data> + <data name="NotAuthorizedPageTitle" xml:space="preserve"> + <value>403</value> </data> <data name="ForbiddenException" xml:space="preserve"> - <value>L'accès à la ressource demandée est interdit</value> + <value>Toegang tot de gevraagde bron is verboden</value> </data> <data name="ResourceValidationException" xml:space="preserve"> - <value>Les données de la demande ne sont pas valides</value> + <value>Aanvraaggegevens zijn niet geldig</value> </data> <data name="RestException" xml:space="preserve"> - <value>Une erreur s'est produite lors de la communication avec le serveur</value> + <value>Er is een fout opgetreden tijdens de communicatie met de server</value> </data> <data name="UnauthorizedException" xml:space="preserve"> - <value>Votre demande ne contient pas d'informations d'authentification valides</value> + <value>Uw aanvraag bevat geen geldige verificatiegegevens</value> </data> <data name="UnknownException" xml:space="preserve"> - <value>Une erreur inconnue s'est produite</value> + <value>Er is een onbekende fout opgetreden</value> </data> <data name="UpdateConcurrencyException" xml:space="preserve"> - <value>L'enregistrement a été modifié par un autre utilisateur après avoir obtenu les données d'origine.</value> + <value>De record is gewijzigd door een Anderse gebruiker nadat u de originele gegevens hebt ontvangen. De operatie is geannuleerd.</value> </data> <data name="ResourceNotFoundException" xml:space="preserve"> - <value>Ressource introuvable</value> + <value>Bron niet gevonden</value> </data> <data name="TooManyRequestsExceptions" xml:space="preserve"> - <value>Trop de demandes</value> + <value>Te veel aanvragen</value> </data> <data name="ServerConnectionException" xml:space="preserve"> - <value>Incapable de se connecter au serveur.</value> + <value>Kan geen verbinding maken met de server.</value> </data> <!-- Boilerplate strings --> <data name="Active" xml:space="preserve"> - <value>Actif</value> + <value>Actief</value> </data> <data name="All" xml:space="preserve"> - <value>Tous</value> + <value>Alle</value> </data> <data name="Alphabetical" xml:space="preserve"> - <value>Alphabétique</value> + <value>A-Z</value> </data> <data name="Completed" xml:space="preserve"> - <value>Complété</value> + <value>Volbracht</value> </data> <data name="Date" xml:space="preserve"> - <value>Date</value> + <value>Datum</value> + </data> + <data name="SettingsPageTitle" xml:space="preserve"> + <value>Instellingen</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>Instellingen</value> + </data> + <data name="Dark" xml:space="preserve"> + <value>Donker</value> + </data> + <data name="Light" xml:space="preserve"> + <value>Licht</value> + </data> + <data name="Language" xml:space="preserve"> + <value>Taal</value> + </data> + <data name="SelectLanguage" xml:space="preserve"> + <value>Taal selecteren</value> </data> <data name="ProfileTitle" xml:space="preserve"> - <value>Profil</value> + <value>Profiel</value> + </data> + <data name="ProfileSubtitle" xml:space="preserve"> + <value>Je profielgegevens bewerken</value> </data> <data name="EditProfileTitle" xml:space="preserve"> - <value>Editer le profil</value> + <value>Profiel bewerken</value> </data> <data name="ChangeEmailTitle" xml:space="preserve"> - <value>Changer l'e-mail</value> + <value>E-mailadres wijzigen</value> </data> <data name="ChangePhoneNumberTitle" xml:space="preserve"> - <value>Changer de numéro de téléphone</value> + <value>Telefoonnummer wijzigen</value> </data> <data name="SuccessfulSendChangeEmailTokenMessage" xml:space="preserve"> - <value>Le jeton d'e-mail de modification vous a été envoyé</value> + <value>Wijzig e-mailtoken is naar u verzonden</value> </data> <data name="SuccessfulSendChangePhoneNumberTokenMessage" xml:space="preserve"> - <value>Le jeton de changement de numéro de téléphone vous a été envoyé</value> + <value>Het token voor het wijzigen van het telefoonnummer is naar u verzonden</value> + </data> + <data name="SuccessfulSendTokenMessage" xml:space="preserve"> + <value>Het token is naar u verzonden</value> + </data> + <data name="SessionsTitle" xml:space="preserve"> + <value>Sessies</value> + </data> + <data name="SessionsSubtitle" xml:space="preserve"> + <value>Uw actieve sessies op meerdere apparaten</value> </data> <data name="UserSessionsTitle" xml:space="preserve"> - <value>Vos séances</value> + <value>Jouw sessies</value> </data> <data name="CurrentSession" xml:space="preserve"> - <value>Session actuelle</value> + <value>Huidige sessie</value> </data> <data name="OtherSessions" xml:space="preserve"> - <value>Autres séances</value> + <value>Anderse sessies</value> </data> <data name="RemoveSession" xml:space="preserve"> - <value>Supprimer la session</value> + <value>Sessie verwijderen</value> </data> <data name="RemoveSessionSuccessMessage" xml:space="preserve"> - <value>Session supprimée avec succès</value> + <value>Sessie succesvol verwijderd</value> + </data> + <data name="NotFoundPageTitle" xml:space="preserve"> + <value>404</value> </data> <data name="NotFoundText" xml:space="preserve"> - <value>Il n'y a rien ici.</value> + <value>Er is hier niets.</value> + </data> + <data name="UnknownDevice" xml:space="preserve"> + <value>Onbekend apparaat</value> </data> - <data name="UnknwonDevice" xml:space="preserve"> - <value>Périphérique inconnu</value> + <data name="UpdateWebViewThroughGooglePlay" xml:space="preserve"> + <value>Werk de webview bij via Google Play.</value> + </data> + <data name="Ok" xml:space="preserve"> + <value>Ok</value> </data> <!--#if (offlineDb == true) --> <data name="OfflineEditProfileTitle" xml:space="preserve"> - <value>Modifier le profil hors ligne</value> - </data> - <!--#endif--> - <!--#if (signalr == true) --> - <data name="TwoFactorTokenPushText" xml:space="preserve"> - <value>{0} est votre jeton à deux facteurs dans Boilerplate.</value> + <value>Offline Profiel bewerken</value> </data> <!--#endif--> <data name="FullName" xml:space="preserve"> - <value>Nom et prénom</value> + <value>Volledige naam</value> + </data> + <data name="TermsPageTitle" xml:space="preserve"> + <value>Voorwaarde</value> </data> - <data name="TermsTitle" xml:space="preserve"> - <value>Termes</value> + <data name="Terms" xml:space="preserve"> + <value>Voorwaarde</value> </data> <data name="ProfileUpdatedSuccessfullyMessage" xml:space="preserve"> - <value>Mise à jour du profil réussie.</value> + <value>Profiel succesvol bijgewerkt.</value> </data> <data name="ConfirmPassword" xml:space="preserve"> - <value>Confirmez le mot de passe</value> + <value>Wachtwoord bevestigen</value> </data> <data name="ConfirmNewPassword" xml:space="preserve"> - <value>Confirmer le nouveau mot de passe</value> + <value>Bevestig nieuw wachtwoord</value> </data> <data name="Email" xml:space="preserve"> - <value>Email</value> + <value>E-mail</value> </data> <data name="CurrentEmail" xml:space="preserve"> - <value>Email actuel</value> + <value>Huidig e-mailadres</value> </data> <data name="NewEmail" xml:space="preserve"> - <value>Nouveau courriel</value> + <value>Nieuwe e-mail</value> </data> <data name="NewEmailPlaceholder" xml:space="preserve"> - <value>Entrez un nouvel e-mail</value> + <value>Voer een nieuw e-mailadres in</value> + </data> + <data name="Phone" xml:space="preserve"> + <value>Telefoon</value> </data> <data name="PhoneNumber" xml:space="preserve"> - <value>Numéro de téléphone</value> + <value>Telefoonnummer</value> </data> <data name="CurrentPhoneNumber" xml:space="preserve"> - <value>Numéro de téléphone actuel</value> + <value>Huidig telefoonnummer</value> </data> <data name="NewPhoneNumber" xml:space="preserve"> - <value>Nouveau numéro de téléphone</value> + <value>Nieuw telefoonnummer</value> </data> <data name="NewPhoneNumberPlaceholder" xml:space="preserve"> - <value>Entrez le nouveau numéro de téléphone</value> + <value>Voer een nieuw telefoonnummer in</value> </data> <data name="GoBack" xml:space="preserve"> - <value>Dos</value> + <value>Terug</value> </data> <data name="Password" xml:space="preserve"> - <value>Mot de passe</value> + <value>Wachtwoord</value> </data> <data name="PasswordPlaceholder" xml:space="preserve"> - <value>Entrer le mot de passe</value> + <value>Wachtwoord invoeren</value> </data> <data name="PasswordChangedSuccessfullyMessage" xml:space="preserve"> - <value>Votre mot de passe a changé avec succès.</value> + <value>Je wachtwoord is gewijzigd.</value> </data> <data name="ResendConfirmationEmailTokenMessage" xml:space="preserve"> - <value>Le jeton de confirmation a été renvoyé à votre adresse e-mail.</value> + <value>Het bevestigingstoken is opnieuw naar uw e-mailadres verzonden.</value> </data> <data name="ResetPasswordTokenSentMessage" xml:space="preserve"> - <value>Le jeton de réinitialisation du mot de passe a été envoyé à votre adresse e-mail.</value> + <value>Het token voor het opnieuw instellen van het wachtwoord is naar uw e-mailadres verzonden.</value> </data> <data name="UserImageCouldNotBeFound" xml:space="preserve"> - <value>L'image de l'utilisateur est introuvable</value> + <value>Gebruikersafbeelding kan niet worden gevonden</value> </data> <data name="Error" xml:space="preserve"> - <value>Erreur</value> - </data> - <data name="TermsAccepted" xml:space="preserve"> - <value>Est-ce que j'accepte les conditions ?</value> + <value>Fout</value> </data> <data name="YouHaveToAcceptTerms" xml:space="preserve"> - <value>Vous devez accepter nos conditions.</value> + <value>U moet akkoord gaan met onze voorwaarden.</value> </data> <data name="AcceptTermsMessage" xml:space="preserve"> - <value>En vous inscrivant, vous acceptez notre</value> + <value>Door u aan te melden, gaat u akkoord met onze</value> </data> <data name="Gender" xml:space="preserve"> - <value>Genre</value> + <value>Geslacht</value> </data> <data name="BirthDate" xml:space="preserve"> - <value>Date de naissance</value> + <value>Geboortedatum</value> </data> <data name="UserName" xml:space="preserve"> - <value>Nom d'utilisateur</value> + <value>Gebruikersnaam</value> </data> <data name="Name" xml:space="preserve"> - <value>Nom</value> + <value>Naam</value> </data> <data name="Description" xml:space="preserve"> - <value>Description</value> + <value>Beschrijving</value> </data> <data name="Price" xml:space="preserve"> - <value>Prix</value> + <value>Prijs</value> </data> <data name="EitherProvideEmailOrPhoneNumber" xml:space="preserve"> - <value>Soit fournir un e-mail ou un numéro de téléphone.</value> + <value>Geef een e-mailadres of telefoonnummer op.</value> </data> <data name="EitherProvideUserNameOrEmailOrPhoneNumber" xml:space="preserve"> - <value>Soit fournissez votre nom d'utilisateur, votre e-mail ou votre numéro de téléphone.</value> + <value>Geef een gebruikersnaam, e-mailadres of telefoonnummer op.</value> </data> <data name="YouNeedToSignIn" xml:space="preserve"> - <value>Vous devez être connecté pour continuer.</value> + <value>U moet zijn ingelogd om door te gaan.</value> </data> <data name="InvalidUserCredentials" xml:space="preserve"> - <value>Nom d'utilisateur ou mot de passe invalide</value> + <value>Ongeldige gebruikersreferenties</value> </data> <data name="EmailAlreadyConfirmed" xml:space="preserve"> - <value>Votre email est déjà confirmé.</value> + <value>Uw e-mailadres is al bevestigd.</value> </data> <data name="PhoneNumberAlreadyConfirmed" xml:space="preserve"> - <value>Votre numéro de téléphone est déjà confirmé.</value> + <value>Je telefoonnummer is al bevestigd.</value> </data> <data name="Title" xml:space="preserve"> - <value>Titre</value> + <value>Titel</value> </data> <data name="AreYouSureWannaDelete" xml:space="preserve"> - <value>Etes-vous sûr de vouloir supprimer {0}?</value> + <value>Weet je zeker dat je {0} wilt verwijderen?</value> </data> <data name="Home" xml:space="preserve"> - <value>Maison</value> + <value>Home</value> + </data> + <data name="Refresh" xml:space="preserve"> + <value>Opfrissen</value> + </data> + <data name="Recover" xml:space="preserve"> + <value>Herstellen</value> + </data> + <data name="SomethingWentWrong" xml:space="preserve"> + <value>Oeps, er is iets misgegaan...</value> + </data> + <data name="BackToHome" xml:space="preserve"> + <value>Terug naar home</value> </data> <data name="UserLockedOut" xml:space="preserve"> - <value>L'utilisateur est verrouillé. Réessayez dans {0}</value> + <value>De gebruiker is buitengesloten. Probeer het opnieuw in {0}</value> </data> <data name="UserIsNotConfirmed" xml:space="preserve"> - <value>L'utilisateur n'est pas confirmé.</value> + <value>Gebruiker is niet bevestigd.</value> </data> <data name="UserNotFound" xml:space="preserve"> - <value>L'utilisateur n'existe pas.</value> + <value>Gebruiker bestaat niet.</value> </data> <data name="DuplicateEmailOrPhoneNumber" xml:space="preserve"> - <value>Cet email ou numéro de téléphone est déjà pris.</value> + <value>Dit e-mailadres of telefoonnummer is al in gebruik.</value> </data> <data name="Add" xml:space="preserve"> - <value>Ajouter</value> + <value>Toevoegen</value> </data> <data name="Token" xml:space="preserve"> - <value>Jeton</value> + <value>Teken</value> </data> <data name="InvalidToken" xml:space="preserve"> - <value>Jeton non valide.</value> + <value>Ongeldig token.</value> </data> <data name="ExpiredToken" xml:space="preserve"> - <value>Jeton expiré.</value> + <value>Verlopen token.</value> </data> <data name="TokenPlaceholder" xml:space="preserve"> - <value>Entrez le jeton</value> + <value>Token invoeren</value> </data> <data name="SignInMessageInSignUp" xml:space="preserve"> - <value>Vous avez déjà un compte?</value> + <value>Heb je al een account?</value> </data> <data name="ConfirmMessageInSignUp" xml:space="preserve"> - <value>Vous souhaitez confirmer un compte?</value> + <value>Wil je een account bevestigen?</value> </data> <data name="ConfirmMessageInProfile" xml:space="preserve"> - <value>Vous avez déjà un jeton?</value> + <value>Heb je al een token?</value> </data> <data name="ConfirmEmailHeaderText" xml:space="preserve"> - <value>Adresse e-mail</value> + <value>E-mailadres</value> </data> <data name="ConfirmEmailSubtitle" xml:space="preserve"> - <value>Nous avons envoyé un jeton de confirmation à votre adresse e-mail.</value> + <value>We hebben een bevestigingstoken naar uw e-mailadres gestuurd.</value> + </data> + <data name="ConfirmPageTitle" xml:space="preserve"> + <value>Bevestigen</value> </data> <data name="ConfirmTitle" xml:space="preserve"> - <value>Confirmez votre compte</value> + <value>Bevestig je account</value> </data> <data name="ConfirmEmailMessage" xml:space="preserve"> - <value>Veuillez confirmer votre e-mail en tapant le jeton ici.</value> + <value>Bevestig uw e-mail door het token hier in te typen.</value> </data> <data name="EmailPlaceholder" xml:space="preserve"> - <value>Entrez l'adresse e-mail</value> + <value>E-mailadres invoeren</value> </data> <data name="EmailToken" xml:space="preserve"> - <value>Jeton de courrier électronique</value> + <value>E-mail token</value> </data> <data name="EmailTokenPlaceholder" xml:space="preserve"> - <value>Entrez le jeton d'e-mail</value> + <value>Voer e-mailtoken in</value> </data> <data name="EmailTokenConfirmButtonText" xml:space="preserve"> - <value>Confirmez votre adresse email</value> + <value>Bevestig e-mailadres</value> </data> <data name="EmailConfirmationSuccessTitle" xml:space="preserve"> - <value>Votre adresse email ({0}) a été confirmée.</value> + <value>Uw e-mailadres({0}) is bevestigd.</value> </data> <data name="EmailConfirmationSuccessMessage" xml:space="preserve"> - <value>Vous pouvez maintenant vous connecter avec votre compte.</value> + <value>U kunt nu inloggen met uw account.</value> </data> <data name="ConfirmPhoneHeaderText" xml:space="preserve"> - <value>Numéro de téléphone</value> + <value>Telefoonnummer</value> </data> <data name="ConfirmPhoneSubtitle" xml:space="preserve"> - <value>Nous avons envoyé un jeton de confirmation sur votre téléphone.</value> + <value>We hebben een bevestigingstoken naar je telefoon gestuurd.</value> </data> <data name="ConfirmPhoneMessage" xml:space="preserve"> - <value>Veuillez confirmer votre numéro de téléphone en tapant le jeton ici.</value> + <value>Bevestig uw telefoonnummer door hier het token in te typen.</value> </data> <data name="PhoneNumberPlaceholder" xml:space="preserve"> - <value>Entrez le numéro de téléphone</value> + <value>Telefoonnummer invoeren</value> </data> <data name="PhoneToken" xml:space="preserve"> - <value>Jeton de téléphone</value> + <value>Telefoon Token</value> </data> <data name="PhoneTokenPlaceholder" xml:space="preserve"> - <value>Entrez le jeton du téléphone</value> + <value>Telefoontoken invoeren</value> </data> <data name="PhoneTokenConfirmButtonText" xml:space="preserve"> - <value>Confirmer le numéro de téléphone</value> + <value>Telefoonnummer bevestigen</value> </data> - <data name="NotReceivedConfirmationPhoneMessage" xml:space="preserve"> - <value>Vous n'avez pas reçu le token sur votre téléphone?</value> + <data name="NotReceivedPhoneMessage" xml:space="preserve"> + <value>Heb je de token niet ontvangen op je telefoon?</value> </data> <data name="ResendConfirmationPhoneTokenMessage" xml:space="preserve"> - <value>Le jeton de confirmation a été renvoyé sur votre téléphone.</value> + <value>Het bevestigingstoken is opnieuw naar uw telefoon verzonden.</value> </data> <data name="ResendPhoneTokenButtonText" xml:space="preserve"> - <value>Renvoyer le jeton de téléphone</value> + <value>Telefoontoken opnieuw verzenden</value> </data> <data name="PhoneConfirmationSuccessTitle" xml:space="preserve"> - <value>Votre numéro de téléphone ({0}) a été confirmé.</value> + <value>Uw telefoonnummer ({0}) is bevestigd.</value> </data> <data name="PhoneConfirmationSuccessMessage" xml:space="preserve"> - <value>Vous pouvez maintenant vous connecter avec votre compte.</value> + <value>U kunt nu inloggen met uw account.</value> + </data> + <data name="AccountTitle" xml:space="preserve"> + <value>Account</value> + </data> + <data name="AccountSubtitle" xml:space="preserve"> + <value>Het e-mailadres of telefoonnummer van je account wijzigen</value> </data> <data name="DeleteAccount" xml:space="preserve"> - <value>Supprimer le compte</value> + <value>Account verwijderen</value> </data> <data name="DeleteAccountPrompt" xml:space="preserve"> - <value>Êtes-vous sûr de vouloir supprimer votre compte ?</value> + <value>Weet je zeker dat je je account wilt verwijderen?</value> </data> <data name="DontHaveAccountMessage" xml:space="preserve"> - <value>Vous n'avez pas de compte ?</value> + <value>Heb je nog geen account?</value> </data> <data name="Edit" xml:space="preserve"> - <value>Modifier</value> + <value>Bewerken</value> </data> <data name="EmailConfirmationFailedMessage" xml:space="preserve"> - <value>Échec de la confirmation par e-mail !</value> + <value>E-mailbevestiging is mislukt!</value> </data> <data name="EmailConfirmationTitle" xml:space="preserve"> - <value>confirmation de l'émail</value> + <value>Bevestiging per e-mail</value> </data> <data name="EmailConfirmedSuccessfullyMessage" xml:space="preserve"> - <value>E-mail confirmé avec succès !</value> + <value>E-mail succesvol bevestigd!</value> </data> <data name="FileUploadFailed" xml:space="preserve"> - <value>Une erreur s'est produite lors du téléchargement du fichier</value> + <value>Er is een fout opgetreden tijdens het uploaden van het bestand</value> </data> - <data name="ForgotPassword" xml:space="preserve"> - <value>Mot de passe oublié</value> + <data name="ForgotPasswordPageTitle" xml:space="preserve"> + <value>Wachtwoord vergeten</value> </data> <data name="ForgotPasswordTitle" xml:space="preserve"> - <value>Mot de passe oublié</value> + <value>Wachtwoord vergeten</value> </data> <data name="ForgotPasswordMessage" xml:space="preserve"> - <value>Veuillez saisir votre adresse e-mail ou votre numéro de téléphone afin que nous puissions vous envoyer un jeton de réinitialisation de mot de passe.</value> + <value>Voer je e-mailadres of telefoonnummer in, zodat we je een token voor het opnieuw instellen van je wachtwoord kunnen sturen. </value> </data> <data name="GenderMale" xml:space="preserve"> - <value>Mâle</value> + <value>Man</value> </data> <data name="GenderOther" xml:space="preserve"> - <value>Autre</value> + <value>Anders</value> </data> <data name="GitHubRepo" xml:space="preserve"> - <value>Dépôt GitHub</value> + <value>GitHub-opslagplaats</value> </data> <data name="GoToToday" xml:space="preserve"> - <value>Allez à aujourd'hui</value> + <value>Ga naar vandaag</value> </data> <data name="HomeMessage" xml:space="preserve"> - <value>Créez facilement votre application Blazor multimode (WASM, serveur, hybride, pré-rendu) dans les plus brefs délais !</value> + <value>Creëer eenvoudig jouw multi-mode (WASM, Server, Hybrid, pre-rendering) Blazor app op de snelste manier ooit!</value> </data> - <data name="HomeTitle" xml:space="preserve"> - <value>Accueil Boilerplate</value> + <data name="HomePageTitle" xml:space="preserve"> + <value>Thuis</value> + </data> + <data name="HomePanelTitle" xml:space="preserve"> + <value>Bouw al uw apps</value> + </data> + <data name="HomePanelSubtitle" xml:space="preserve"> + <value>gebruik makend van wat je al kent en leuk vindt</value> + </data> + <data name="WatchVideo" xml:space="preserve"> + <value>Bekijk video</value> + </data> + <data name="LearnMore" xml:space="preserve"> + <value>Meer informatie</value> + </data> + <data name="BitPlatformMessage" xml:space="preserve"> + <value>Handige tools voor .NET-ontwikkelaars op meerdere platforms</value> + </data> + <data name="BitBlazorUIMessage" xml:space="preserve"> + <value>Krachtige, eenvoudig aan te passen en mooie Blazor-componenten</value> + </data> + <data name="BitProjectTemplateMessage" xml:space="preserve"> + <value>Een veelzijdige .NET-projectsjabloon die overal naadloos werkt</value> </data> <data name="DescriptionMetaTagValue" xml:space="preserve"> - <value>Le Boilerplate est construit avec ASP.NET Core, Identity, Web API, EF Core et Blazor.</value> + <value>De Boilerplate is gebouwd met ASP.NET Core, Identity, Web API, EF Core en Blazor.</value> </data> <data name="InvalidConfirmationTokenMessage" xml:space="preserve"> - <value>Il semble que le lien de confirmation soit invalide ou ait expiré.</value> + <value>Het lijkt erop dat het bevestigingstoken ongeldig is of is verlopen.</value> </data> <data name="NewPassword" xml:space="preserve"> - <value>nouveau mot de passe</value> + <value>Nieuw wachtwoord</value> </data> <data name="OldPassword" xml:space="preserve"> - <value>Old Password</value> + <value>Oud wachtwoord</value> </data> <data name="No" xml:space="preserve"> - <value>Non</value> + <value>Nee</value> </data> - <data name="NotReceivedConfirmationEmailMessage" xml:space="preserve"> - <value>Vous n'avez pas reçu l'e-mail de confirmation ?</value> + <data name="NotReceivedEmailMessage" xml:space="preserve"> + <value>Heeft u de e-mail niet ontvangen?</value> </data> <data name="Or" xml:space="preserve"> - <value>Ou</value> + <value>of</value> </data> <data name="ProfileImage" xml:space="preserve"> - <value>Image de profil</value> + <value>Profielafbeelding</value> </data> <data name="Remove" xml:space="preserve"> - <value>Retirer</value> + <value>Verwijderen</value> </data> <data name="ResendEmailTokenButtonText" xml:space="preserve"> - <value>Renvoyer le jeton d'e-mail</value> + <value>E-mailtoken opnieuw verzenden</value> + </data> + <data name="ResetPasswordPageTitle" xml:space="preserve"> + <value>Wachtwoord opnieuw instellen</value> + </data> + <data name="ResetPassword" xml:space="preserve"> + <value>Wachtwoord opnieuw instellen</value> </data> <data name="ResetPasswordTitle" xml:space="preserve"> - <value>Réinitialiser le mot de passe</value> + <value>Wachtwoord opnieuw instellen</value> </data> <data name="ResetPasswordSubtitle" xml:space="preserve"> - <value>Nous avons envoyé un jeton sur votre téléphone ou par e-mail.</value> + <value>We hebben een token naar je telefoon of e-mail gestuurd.</value> </data> <data name="ResetPasswordMessage" xml:space="preserve"> - <value>Veuillez soumettre le jeton avec votre nouveau mot de passe ici.</value> + <value>Geef hier het token samen met je nieuwe wachtwoord hier door. </value> </data> <data name="ResetPasswordButtonText" xml:space="preserve"> - <value>Définir le mot de passe</value> + <value>Wachtwoord instellen</value> </data> <data name="ResetPasswordSuccessTitle" xml:space="preserve"> - <value>Votre mot de passe a été réinitialisé.</value> + <value>Uw wachtwoord is opnieuw ingesteld.</value> </data> <data name="ResetPasswordSuccessBody" xml:space="preserve"> - <value>Vous pouvez maintenant vous connecter avec votre mot de passe.</value> + <value>U kunt nu inloggen met uw wachtwoord.</value> </data> <data name="ResetPasswordMessageInForgot" xml:space="preserve"> - <value>Vous disposez déjà d'un jeton de réinitialisation de mot de passe?</value> + <value>Heb je al een token voor het opnieuw instellen van je wachtwoord?</value> </data> <data name="Save" xml:space="preserve"> - <value>Sauvegarder</value> + <value>Opslaan</value> + </data> + <data name="SignInPageTitle" xml:space="preserve"> + <value>Inloggen</value> + </data> + <data name="SignInPanelTitle" xml:space="preserve"> + <value>Welkom terug!</value> </data> - <data name="SignInTitle" xml:space="preserve"> - <value>Se connecter</value> + <data name="SignInPanelSubtitle" xml:space="preserve"> + <value>Log in bij jouw account met</value> </data> <data name="SocialSignedInTitle" xml:space="preserve"> - <value>Connecté sociale</value> + <value>Aangemeld bij social media</value> + </data> + <data name="Continue" xml:space="preserve"> + <value>Verzenden</value> </data> <data name="SocialSignedInMessageTitle" xml:space="preserve"> - <value>La connexion sociale est terminée</value> + <value>De sociale aanmelding is voltooid</value> </data> <data name="SocialSignedInMessageBody" xml:space="preserve"> - <value>vous pouvez revenir à l'application maintenant</value> + <value>U kunt nu teruggaan naar de applicatie</value> </data> <data name="GoogleSignInButtonText" xml:space="preserve"> - <value>Connectez-vous avec Google</value> + <value>Inloggen met Google</value> </data> <data name="GitHubSignInButtonText" xml:space="preserve"> - <value>Connectez-vous avec GitHub</value> + <value>Inloggen met GitHub</value> </data> <data name="TwitterSignInButtonText" xml:space="preserve"> - <value>Connectez-vous avec Twitter (X)</value> + <value>Inloggen met Twitter (X)</value> </data> <data name="GoogleSignUpButtonText" xml:space="preserve"> - <value>Inscrivez-vous avec Google</value> + <value>Inloggen met Google</value> </data> <data name="GitHubSignUpButtonText" xml:space="preserve"> - <value>Inscrivez-vous avec GitHub</value> + <value>Inloggen met GitHub</value> </data> <data name="TwitterSignUpButtonText" xml:space="preserve"> - <value>Inscrivez-vous avec Twitter (X)</value> + <value>Meld je aan met Twitter (X)</value> </data> <data name="SignOut" xml:space="preserve"> - <value>se déconnecter</value> + <value>Afmelden</value> </data> <data name="SignOutPrompt" xml:space="preserve"> - <value>Êtes-vous certain de vouloir vous déconnecter?</value> + <value>Weet je zeker dat je je wilt afmelden?</value> </data> <data name="SignUp" xml:space="preserve"> - <value>S'inscrire</value> + <value>Registreren</value> + </data> + <data name="SignUpPageTitle" xml:space="preserve"> + <value>Registreren</value> </data> - <data name="SingUpTitle" xml:space="preserve"> - <value>S'inscrire</value> + <data name="SignUpPanelTitle" xml:space="preserve"> + <value>Ga vandaag nog aan de slag</value> + </data> + <data name="SignUpPanelSubtitle" xml:space="preserve"> + <value>Maak een nieuw account aan bij</value> </data> <data name="Submit" xml:space="preserve"> - <value>Soumettre</value> + <value>Opslaan</value> </data> <data name="TermsMessage" xml:space="preserve"> - <value>je suis d'accord avec le</value> + <value>Ik ga akkoord met de</value> </data> <data name="UploadNewProfileImage" xml:space="preserve"> - <value>Téléchargez une nouvelle image de profil</value> + <value>Upload een nieuwe profielafbeelding</value> </data> <data name="Yes" xml:space="preserve"> - <value>Oui</value> + <value>Ja</value> </data> <data name="SelectBirthDate" xml:space="preserve"> - <value>Sélectionnez votre date de naissance</value> - </data> - <data name="ResetPassword" xml:space="preserve"> - <value>Réinitialiser le mot de passe</value> + <value>Selecteer je geboortedatum</value> </data> <data name="Action" xml:space="preserve"> - <value>Action</value> + <value>Actie</value> </data> <data name="Back" xml:space="preserve"> - <value>Dos</value> + <value>Terug</value> </data> <data name="Cancel" xml:space="preserve"> - <value>Annuler</value> + <value>Annuleren</value> </data> <data name="CheckSpamMailMessage" xml:space="preserve"> - <value>Vérifiez vos spams/indésirables si vous ne les trouvez pas dans la boîte de réception.</value> + <value>Controleer uw spam/ongewenste e-mail als u deze niet kunt vinden in de inbox.</value> </data> <data name="Color" xml:space="preserve"> - <value>Couleur</value> + <value>Kleur</value> </data> <data name="CustomColor" xml:space="preserve"> - <value>Couleur personnalisée</value> + <value>Aangepaste kleur</value> </data> <data name="Delete" xml:space="preserve"> - <value>Supprimer</value> + <value>Verwijderen</value> </data> <data name="Id" xml:space="preserve"> - <value>Identifiant</value> + <value>Legitimatiebewijs</value> </data> <data name="SignIn" xml:space="preserve"> - <value>Se connecter</value> + <value>Inloggen</value> </data> <data name="Confirm" xml:space="preserve"> - <value>Confirmer</value> + <value>Bevestigen</value> </data> <data name="SignInAsDifferentUser" xml:space="preserve"> - <value>Connectez-vous en tant qu'utilisateur différent</value> + <value>Inloggen als Anderse gebruiker</value> </data> <data name="YouAreSignInAs" xml:space="preserve"> - <value>Vous êtes connecté en tant que</value> + <value>U meldt zich aan als</value> </data> <data name="ForgotPasswordLink" xml:space="preserve"> - <value>Mot de passe oublié?</value> + <value>Wachtwoord vergeten?</value> </data> <data name="GenderFemale" xml:space="preserve"> - <value>Femelle</value> + <value>Vrouw</value> </data> <data name="RememberMe" xml:space="preserve"> - <value>Souviens-toi de moi?</value> + <value>Onthoud mij.</value> + </data> + <data name="Copy" xml:space="preserve"> + <value>Kopiëren</value> + </data> + <data name="Copied" xml:space="preserve"> + <value>Gekopieerd</value> + </data> + <data name="TfaTitle" xml:space="preserve"> + <value>Twee-factor-authenticatie</value> + </data> + <data name="TfaSubtitle" xml:space="preserve"> + <value>De meervoudige verificatie van uw account beheren</value> </data> <data name="TwoFactorCode" xml:space="preserve"> - <value>Code d'authentification</value> + <value>2FA-code</value> </data> <data name="TwoFactorRecoveryCode" xml:space="preserve"> - <value>Code de récupération</value> + <value>Herstelcode</value> + </data> + <data name="TfaPanelTitle" xml:space="preserve"> + <value>2-Factor Authenticatie</value> + </data> + <data name="TfaPanelSubtitle" xml:space="preserve"> + <value>Haal een nieuwe code op via je authenticator-app of gebruik je herstelcode.</value> + </data> + <data name="TfaPanelAnotherWayTitle" xml:space="preserve"> + <value>Probeer een Anderse manier</value> + </data> + <data name="TfaPanelAnotherWaySubtitle" xml:space="preserve"> + <value>Je kunt een nieuwe code krijgen via je e-mail of telefoon..</value> + </data> + <data name="TfaPanelAnotherWayGetCode" xml:space="preserve"> + <value>Code ophalen</value> </data> <data name="TwoFactorAuthTitle" xml:space="preserve"> <value>2FA</value> </data> <data name="TfaResetSharedKeyError" xml:space="preserve"> - <value>La réinitialisation de la clé partagée 2fa doit désactiver 2fa jusqu'à ce qu'un jeton 2fa basé sur la nouvelle clé partagée soit validé.</value> + <value>Het resetten van de gedeelde 2fa-sleutel moet 2fa uitschakelen totdat een 2fa-token op basis van de nieuwe gedeelde sleutel is gevalideerd.</value> </data> <data name="TfaEmptyCodeError" xml:space="preserve"> - <value>Aucun jeton 2fa n'a été fourni par la demande. Un jeton 2fa valide est requis pour activer 2fa.</value> + <value>Er werd geen 2fa-token verstrekt door het verzoek. Een geldig 2fa-token is vereist om 2fa in te schakelen.</value> </data> <data name="TfaInvalidCodeError" xml:space="preserve"> - <value>Le jeton 2fa fourni par la demande n'était pas valide. Un jeton 2fa valide est requis pour activer 2fa.</value> + <value>Het 2fa-token dat in het verzoek werd verstrekt, was ongeldig. Een geldig 2fa-token is vereist om 2fa in te schakelen.</value> </data> <data name="TfaProtectedSignInSubtitle" xml:space="preserve"> - <value>Votre connexion est protégée par une application d'authentification.</value> + <value>Je login is beveiligd met een authenticator app.</value> </data> <data name="TfaEnterCodeInSignInMessage" xml:space="preserve"> - <value>Entrez votre code d'authentification:</value> + <value>Voer je authenticatorcode in:</value> </data> <data name="TfaEnterRecoveryCodeInSignInMessage" xml:space="preserve"> - <value>Ou entrez votre code de récupération:</value> + <value>Of voer je herstelcode in:</value> </data> <data name="TfaAuthenticatorAppVerifiedMessage" xml:space="preserve"> - <value>Votre application d'authentification a été vérifiée.</value> + <value>Je authenticator-app is geverifieerd.</value> </data> <data name="TfaConfigureAutAppTitle" xml:space="preserve"> - <value>Configurer l'application d'authentification</value> + <value>Authenticator-app configureren</value> </data> <data name="TfaConfigureAutAppSubtitle" xml:space="preserve"> - <value>Pour utiliser une application d'authentification, suivez les étapes suivantes:</value> + <value>Als je een authenticator-app wilt gebruiken, doorloop je de volgende stappen:</value> </data> <data name="TfaConfigureAutAppStep1" xml:space="preserve"> - <value>Téléchargez une application d'authentification à deux facteurs comme Google Authenticator pour {0} et {1}.</value> + <value>Download een tweefactorauthenticatie-app zoals Google Authenticator voor {0} en {1}.</value> </data> <data name="TfaConfigureAutAppStep2" xml:space="preserve"> - <value>Scannez/click le code QR ou entrez la clé suivante dans votre application d'authentification à deux facteurs (les espaces et la casse n'ont pas d'importance):</value> + <value>Scan/klik op de QR-code of voer de volgende sleutel in uw tweefactorauthenticator-app in (spaties en behuizing doen er niet toe):</value> </data> <data name="TfaConfigureAutAppStep3" xml:space="preserve"> - <value>Une fois que vous avez scanné le code QR ou saisi la clé ci-dessus, votre application d'authentification à deux facteurs vous fournira un code unique. Entrez le code dans la case de confirmation ci-dessous.</value> + <value>Nadat je de QR-code hebt gescand of de bovenstaande sleutel hebt ingevoerd, geeft je tweefactorauthenticatie-app je een unieke code. Voer de code in het bevestigingsvak hieronder in.</value> </data> <data name="TfaConfigureAutAppVerificationCodeLabel" xml:space="preserve"> - <value>Le code de vérification:</value> + <value>Verificatie code:</value> </data> <data name="TfaConfigureAutAppVerificationCodePlaceholder" xml:space="preserve"> - <value>Entrez le code de vérification.</value> + <value>Voer de verificatiecode in.</value> </data> <data name="TfaConfigureAutAppVerifyButtonText" xml:space="preserve"> - <value>Vérifier</value> + <value>Verifiëren</value> </data> <data name="TfaRecoveryCodesHeader" xml:space="preserve"> - <value>Codes de récupération</value> + <value>Terugwinning</value> </data> <data name="TfaRecoveryCodesZeroLeftTitle" xml:space="preserve"> - <value>Vous n'avez plus de codes de récupération.</value> + <value>Je hebt geen herstelcodes meer over.</value> </data> <data name="TfaRecoveryCodesZeroLeftSubtitle" xml:space="preserve"> - <value>Vous devez générer un nouvel ensemble de codes de récupération avant de pouvoir vous connecter avec un code de récupération.</value> + <value>U moet een nieuwe set herstelcodes genereren voordat u kunt inloggen met een herstelcode.</value> </data> <data name="TfaRecoveryCodesOneLeftTitle" xml:space="preserve"> - <value>Il vous reste 1 code de récupération.</value> + <value>Je hebt nog 1 herstelcode over.</value> </data> <data name="TfaRecoveryCodesOneLeftSubtitle" xml:space="preserve"> - <value>Vous pouvez générer un nouvel ensemble de codes de récupération.</value> + <value>U kunt een nieuwe set herstelcodes genereren.</value> </data> <data name="TfaRecoveryCodesThreeLeftTitle" xml:space="preserve"> - <value>Il vous reste {0} codes de récupération.</value> + <value>Je hebt nog {0} herstelcodes over.</value> </data> <data name="TfaRecoveryCodesThreeLeftSubtitle" xml:space="preserve"> - <value>Vous devez générer un nouvel ensemble de codes de récupération.</value> + <value>U moet een nieuwe set herstelcodes genereren.</value> </data> <data name="TfaRecoveryCodesGenerateWraning" xml:space="preserve"> - <value>La génération de nouveaux codes de récupération ne modifie pas les clés utilisées dans les applications d'authentification. Si vous souhaitez modifier la clé utilisée dans une application d'authentification, vous devez réinitialiser vos clés d'authentification dans l'onglet suivant.</value> + <value>Het genereren van nieuwe herstelcodes heeft geen invloed op de sleutels die worden gebruikt in authenticator-apps. Als u de sleutel wilt wijzigen die in een authenticator-app wordt gebruikt, moet u uw authenticatorsleutels in het volgende tabblad opnieuw instellen.</value> </data> <data name="TfaRecoveryCodesGenerateButtonText" xml:space="preserve"> - <value>Générer des codes de récupération</value> + <value>Herstelcodes genereren</value> </data> <data name="TfaRecoveryCodesWarningTitle" xml:space="preserve"> - <value>Conservez ces codes dans un endroit sûr.</value> + <value>Bewaar deze codes op een veilige plaats.</value> </data> <data name="TfaRecoveryCodesWarning" xml:space="preserve"> - <value>Si vous perdez votre appareil d'authentification et ne disposez pas des codes de récupération, vous perdrez l'accès à votre compte.</value> + <value>Als je je authenticatorapparaat verliest en je hebt de herstelcodes niet, heb je geen toegang meer tot je account.</value> </data> <data name="TfaRecoveryCodesTitle" xml:space="preserve"> - <value>Codes de récupération:</value> + <value>Herstel codes:</value> </data> <data name="TfaAuthAppHeader" xml:space="preserve"> - <value>Application d'authentification</value> + <value>Verificator</value> </data> <data name="TfaAuthAppWarningTitle" xml:space="preserve"> - <value>Si vous réinitialisez votre clé d'authentification, votre application d'authentification ne fonctionnera pas tant que vous ne la reconfigurerez pas.</value> + <value>Als je je authenticatorsleutel opnieuw instelt, werkt je authenticator-app pas als je deze opnieuw configureert.</value> </data> <data name="TfaAuthAppWarning" xml:space="preserve"> - <value>Ce processus désactive 2FA jusqu'à ce que vous vérifiiez votre application d'authentification. Si vous ne terminez pas la configuration de votre application d'authentification, vous risquez de perdre l'accès à votre compte.</value> + <value>Dit proces schakelt 2FA uit totdat je je authenticator-app verifieert. Als je de configuratie van je authenticator-app niet voltooit, heb je mogelijk geen toegang meer tot je account.</value> </data> <data name="TfaAuthAppResetKeyButtonText" xml:space="preserve"> - <value>Réinitialiser la clé d'authentification</value> + <value>Authenticatorsleutel resetten</value> </data> <data name="TfaDisable2faHeader" xml:space="preserve"> - <value>Désactiver 2FA</value> + <value>Uitschakelen</value> </data> <data name="TfaDisable2faWarningTitle" xml:space="preserve"> - <value>Cette action désactive uniquement 2FA.</value> + <value>Met deze actie wordt alleen 2FA uitgeschakeld.</value> </data> <data name="TfaDisable2faWarning" xml:space="preserve"> - <value>La désactivation de 2FA ne modifie pas les clés utilisées dans les applications d'authentification. Si vous souhaitez modifier la clé utilisée dans une application d'authentification, vous devez réinitialiser vos clés d'authentification dans l'onglet précédent.</value> + <value>Het uitschakelen van 2FA verAnderst niets aan de sleutels die worden gebruikt in authenticator-apps. Als u de sleutel wilt wijzigen die in een authenticator-app wordt gebruikt, moet u uw authenticatorsleutels in het vorige tabblad opnieuw instellen.</value> </data> <data name="TfaDisable2faButtonText" xml:space="preserve"> - <value>Désactiver 2FA</value> + <value>Schakel 2FA uit</value> </data> <data name="TwoFactorToken" xml:space="preserve"> - <value>2FA Token</value> + <value>2FA-teken</value> </data> <data name="TfaTokenSignInTitle" xml:space="preserve"> - <value>Pas d'accès à l'application d'authentification?</value> + <value>Geen toegang tot de authenticator-app?</value> </data> <data name="TfaTokenGenerateButtonText" xml:space="preserve"> - <value>Générer et envoyer un jeton 2FA</value> + <value>2FA-token genereren en verzenden</value> </data> <data name="TfaTokenSentMessage" xml:space="preserve"> - <value>Le token 2FA a été généré et vous a été envoyé.</value> + <value>Het 2FA-token is gegenereerd en naar u verzonden.</value> </data> <data name="WaitForEmailTokenRequestResendDelay" xml:space="preserve"> - <value>Vous avez déjà demandé l'e-mail de confirmation.</value> + <value>Je hebt de bevestigingsmail al aangevraagd. Probeer het opnieuw in {0}</value> </data> <data name="WaitForPhoneNumberTokenRequestResendDelay" xml:space="preserve"> - <value>Vous avez déjà demandé le sms de confirmation. Réessayez dans {0}</value> + <value>Je hebt de bevestigings-sms al aangevraagd. Probeer het opnieuw in {0}</value> </data> <data name="WaitForResetPasswordTokenRequestResendDelay" xml:space="preserve"> - <value>Vous avez déjà demandé le jeton de réinitialisation du mot de passe. Réessayez dans {0}</value> + <value>U hebt het token voor het opnieuw instellen van het wachtwoord al aangevraagd. Probeer het opnieuw in {0}</value> </data> <data name="WaitForOtpRequestResendDelay" xml:space="preserve"> - <value>Vous avez déjà demandé le mot de passe à usage unique. Réessayez dans {0}</value> + <value>Je hebt al een OTP aangevraagd. Probeer het opnieuw in {0}</value> </data> <data name="Otp" xml:space="preserve"> - <value>OTP</value> + <value>OTP (Computergebruik)</value> + </data> + <data name="OtpPanelTitle" xml:space="preserve"> + <value>Controleer uw {0}</value> + </data> + <data name="OtpPanelSubtitle" xml:space="preserve"> + <value>Bevestig uw {0} door de code in te voeren die naar {1} is verzonden</value> + </data> + <data name="Code" xml:space="preserve"> + <value>Code</value> + </data> + <data name="OtpResendMessage" xml:space="preserve"> + <value>Heb je de code niet ontvangen?</value> + </data> + <data name="Resend" xml:space="preserve"> + <value>Verzenden</value> </data> <data name="OtpPlaceholder" xml:space="preserve"> - <value>Entrez OTP</value> + <value>Voer OTP in</value> </data> <data name="OtpSentMessage" xml:space="preserve"> - <value>Un OTP a été généré et vous a été envoyé.</value> + <value>Er is een OTP gegenereerd en naar u verzonden.</value> </data> <data name="SendOtpButtonText" xml:space="preserve"> - <value>Envoyer un lien OTP et Magic</value> + <value>Stuur OTP en Magic link</value> </data> <data name="OtpEmailOrPhoneNumberSubtitle" xml:space="preserve"> - <value>Entrez votre email ou votre numéro de téléphone:</value> + <value>Voer uw e-mailadres of telefoonnummer in:</value> </data> <data name="EitherProvidePasswordOrOtp" xml:space="preserve"> - <value>Soit fournir un mot de passe, soit Otp</value> + <value>Geef een wachtwoord of OTP op</value> </data> <data name="WaitForTwoFactorTokenRequestResendDelay" xml:space="preserve"> - <value>Vous avez déjà demandé l'e-mail du token 2FA. Réessayez en {0}.</value> + <value>Je hebt de e-mail met de 2FA-token al aangevraagd. Probeer het opnieuw in {0}.</value> </data> <data name="ChangePhoneNumberTokenSmsText" xml:space="preserve"> - <value>{0} est votre jeton de changement de numéro de téléphone dans Boilerplate.</value> + <value>{0} is het token voor het wijzigen van uw telefoonnummer in Boilerplate.</value> </data> <data name="ConfirmPhoneTokenSmsText" xml:space="preserve"> - <value>{0} est votre jeton de confirmation de numéro de téléphone dans Boilerplate.</value> + <value>{0} is uw bevestigingstelefoonnummer token in Boilerplate.</value> </data> - <data name="TwoFactorTokenSmsText" xml:space="preserve"> - <value>{0} est votre jeton à deux facteurs dans Boilerplate.</value> + <data name="TwoFactorTokenShortText" xml:space="preserve"> + <value>{0} is je tweefactortoken in Boilerplate.</value> </data> - <data name="OtpSmsText" xml:space="preserve"> - <value>{0} est votre mot de passe à usage unique dans Boilerplate.</value> + <data name="OtpShortText" xml:space="preserve"> + <value>{0} is uw OTP in Boilerplate.</value> </data> - <data name="ResetPasswordTokenSmsText" xml:space="preserve"> - <value>{0} est votre jeton de réinitialisation de mot de passe dans Boilerplate.</value> + <data name="ResetPasswordTokenShortText" xml:space="preserve"> + <value>{0} is het token voor het opnieuw instellen van uw wachtwoord in Boilerplate.</value> </data> <data name="Online" xml:space="preserve"> - <value>En ligne</value> + <value>Online</value> </data> <data name="Recently" xml:space="preserve"> - <value>Récemment</value> + <value>Onlangs</value> </data> <data name="WaitForRevokeSessionDelay" xml:space="preserve"> - <value>Vous ne pouvez pas révoquer d'autres sessions pour le moment. Réessayez dans {0}</value> + <value>Je kunt Anderse sessies op dit moment niet intrekken. Probeer het opnieuw in {0}</value> </data> <data name="Update" xml:space="preserve"> - <value>Mise à jour</value> + <value>Update</value> </data> <data name="NewVersionIsAvailable" xml:space="preserve"> - <value>Une nouvelle version est disponible</value> + <value>Nieuwe versie is beschikbaar</value> </data> <data name="UpdateToNewVersion" xml:space="preserve"> - <value>Souhaitez-vous mettre à jour vers la nouvelle version?</value> + <value>Wilt u updaten naar de nieuwe versie?</value> </data> <data name="AboutTitle" xml:space="preserve"> - <value>À propos</value> + <value>Over</value> + </data> + <data name="NothingFound" xml:space="preserve"> + <value>Niets gevonden!</value> </data> <!--#if (sample == "Todo") --> <data name="TodoAddPlaceholder" xml:space="preserve"> - <value>Ajouter une tâche</value> + <value>Een To do toevoegen</value> </data> <data name="NoTodos" xml:space="preserve"> - <value>Aucune tâche pour l'instant</value> + <value>Nog geen todo's</value> </data> <data name="TodoSearchPlaceholder" xml:space="preserve"> - <value>Rechercher des choses à faire...</value> + <value>Zoek todo...</value> </data> <data name="ToDoItemCouldNotBeFound" xml:space="preserve"> - <value>L'élément à faire est introuvable</value> + <value>Todo-item kan niet worden gevonden</value> </data> - <data name="TodoTitle" xml:space="preserve"> - <value>Faire</value> + <data name="TodoPageTitle" xml:space="preserve"> + <value>To do</value> + </data> + <data name="Todo" xml:space="preserve"> + <value>To do</value> </data> <data name="DeleteTodoItem" xml:space="preserve"> - <value>Supprimer l'élément à faire</value> + <value>Takenitem verwijderen</value> </data> <!--#elif (sample == "Admin") --> + <data name="AdminPanel" xml:space="preserve"> + <value>Beheerderspaneel</value> + </data> <data name="ProductsCount" xml:space="preserve"> - <value>Les produits comptent</value> + <value>Aantal producten</value> </data> <data name="Last30DaysProductCount" xml:space="preserve"> - <value>Nombre de produits des 30 derniers jours</value> + <value>Aantal producten in de afgelopen 30 dagen</value> </data> <data name="AddProduct" xml:space="preserve"> - <value>Ajouter un produit</value> + <value>Product toevoegen</value> </data> <data name="SelectCategory" xml:space="preserve"> - <value>Choisir une catégorie</value> + <value>Selecteer categorie</value> </data> <data name="TotalCategories" xml:space="preserve"> - <value>Catégories totales</value> + <value>Totaal categorieën</value> </data> <data name="TotalProducts" xml:space="preserve"> - <value>Produits totaux</value> + <value>Totaal producten</value> </data> <data name="EditCategory" xml:space="preserve"> - <value>Modifier la catégorie</value> + <value>Categorie bewerken</value> </data> <data name="AddCategory" xml:space="preserve"> - <value>Nouvelle catégorie</value> + <value>Nieuwe categorie</value> </data> <data name="CategoriesPageTitle" xml:space="preserve"> - <value>Catégories</value> + <value>Categorieën</value> </data> <data name="EnterCategoryName" xml:space="preserve"> - <value>Entrez le nom de la catégorie</value> + <value>Voer de categorienaam in</value> </data> <data name="EnterProductName" xml:space="preserve"> - <value>Entrez le nom du produit</value> + <value>Voer de productnaam in</value> </data> - <data name="Last30DaysCategoryCount" xml:space="preserve"> - <value>Nombre de catégories des 30 derniers jours</value> + <data name="CategoriesWithProductCount" xml:space="preserve"> + <value>Aantal categorieën met product</value> </data> <data name="ProductSales" xml:space="preserve"> - <value>Ventes de produits</value> + <value>Verkoop van producten</value> </data> <data name="ProductSalesText" xml:space="preserve"> - <value>Ce graphique montre le numéro de vente de chaque produit.</value> + <value>Deze grafiek toont het aantal verkochte items van elk product..</value> </data> <data name="SearchOnName" xml:space="preserve"> - <value>Rechercher sur le nom</value> + <value>Zoeken op naam</value> </data> <data name="ProductsCountPerCategoryChart" xml:space="preserve"> - <value>Tableau du nombre de produits par catégorie</value> + <value>Grafiek Aantal producten per categorie</value> </data> <data name="ProductsCountPerCategoryChartText" xml:space="preserve"> - <value>Ce graphique montre le nombre de produits dans chaque catégorie.</value> + <value>Deze grafiek toont het aantal producten in elke categorie.</value> </data> <data name="ProductsPageTitle" xml:space="preserve"> - <value>Des produits</value> + <value>Producten</value> </data> <data name="ProductsPercentagePerCategory" xml:space="preserve"> - <value>Pourcentage de produits par catégorie</value> + <value>Productenpercentage per categorie</value> </data> <data name="ProductsPercentagePerCategoryText" xml:space="preserve"> - <value>Ce graphique montre le pourcentage de produits dans chaque catégorie.</value> + <value>Deze grafiek toont het percentage producten in elke categorie.</value> </data> <data name="Categories" xml:space="preserve"> - <value>Catégories</value> + <value>Categorieën</value> </data> <data name="ProductCategory" xml:space="preserve"> - <value>Catégorie de produit</value> + <value>Productcategorie</value> </data> <data name="Products" xml:space="preserve"> - <value>Des produits</value> + <value>Producten</value> </data> <data name="ProductCouldNotBeFound" xml:space="preserve"> - <value>L'entité du produit est introuvable</value> + <value>Productentiteit kan niet worden gevonden</value> </data> <data name="CategoryNotEmpty" xml:space="preserve"> - <value>Cette catégorie contient certains produits, vous ne pouvez donc pas la supprimer</value> + <value>Deze categorie bevat enkele producten, dus u kunt deze niet verwijderen</value> </data> <data name="AreYouSureWannaDeleteCategory" xml:space="preserve"> - <value>Etes-vous sûr de vouloir supprimer la catégorie {0}</value> + <value>Weet u zeker dat u categorie {0} wilt verwijderen?</value> </data> <data name="AreYouSureWannaDeleteProduct" xml:space="preserve"> - <value>Êtes-vous sûr de vouloir supprimer le produit {0}</value> + <value>Weet u zeker dat u product wilt verwijderen {0}</value> </data> <data name="DeleteCategory" xml:space="preserve"> - <value>Supprimer la catégorie</value> + <value>Categorie verwijderen</value> </data> <data name="DeleteProduct" xml:space="preserve"> - <value>Supprimer le produit</value> + <value>Product verwijderen</value> </data> <data name="CategoryCouldNotBeFound" xml:space="preserve"> - <value>L'entité de catégorie est introuvable</value> + <value>Categorie-entiteit kan niet worden gevonden</value> </data> <data name="Category" xml:space="preserve"> - <value>Catégorie</value> + <value>Categorie</value> </data> <data name="DefaultColorPicker" xml:space="preserve"> - <value>Sélecteur de couleurs par défaut</value> + <value>Standaard kleurkiezer</value> </data> <data name="EditProduct" xml:space="preserve"> - <value>Modifier le produit</value> + <value>Product bewerken</value> + </data> + <data name="DashboardPageTitle" xml:space="preserve"> + <value>Dashboard</value> </data> <data name="Dashboard" xml:space="preserve"> - <value>Tableau de bord</value> + <value>Dashboard</value> + </data> + <data name="DashboardSubtitle" xml:space="preserve"> + <value>Dit zijn jouw analytische gegevens</value> </data> <!--#if (captcha == "reCaptcha") --> <data name="InvalidGoogleRecaptchaResponse" xml:space="preserve"> - <value>Réponse Google reCAPTCHA non valide.</value> + <value>Ongeldig Google reCAPTCHA-antwoord.</value> </data> <data name="InvalidGoogleRecaptchaChallenge" xml:space="preserve"> - <value>Vous devez réussir le défi Google reCAPTCHA.</value> + <value>U moet slagen voor de Google reCAPTCHA-uitdaging.</value> </data> <!--#endif --> <!--#endif --> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx index 67a6d8a7b2..3c21d04baa 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx @@ -146,6 +146,9 @@ </data> <data name="ConflictException" xml:space="preserve"> <value>Request could not be processed because of conflict in the request</value> + </data> + <data name="NotAuthorizedPageTitle" xml:space="preserve"> + <value>403</value> </data> <data name="ForbiddenException" xml:space="preserve"> <value>Access to the requested resource is forbidden</value> @@ -182,16 +185,40 @@ <value>All</value> </data> <data name="Alphabetical" xml:space="preserve"> - <value>Alphabetical</value> + <value>A-Z</value> </data> <data name="Completed" xml:space="preserve"> <value>Completed</value> </data> <data name="Date" xml:space="preserve"> <value>Date</value> + </data> + <data name="SettingsPageTitle" xml:space="preserve"> + <value>Settings</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>Settings</value> + </data> + <data name="Dark" xml:space="preserve"> + <value>Dark</value> + </data> + <data name="Light" xml:space="preserve"> + <value>Light</value> + </data> + <data name="Language" xml:space="preserve"> + <value>Language</value> + </data> + <data name="SelectLanguage" xml:space="preserve"> + <value>Select language</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>Settings</value> </data> <data name="ProfileTitle" xml:space="preserve"> <value>Profile</value> + </data> + <data name="ProfileSubtitle" xml:space="preserve"> + <value>Edit your profile data</value> </data> <data name="EditProfileTitle" xml:space="preserve"> <value>Edit profile</value> @@ -207,6 +234,15 @@ </data> <data name="SuccessfulSendChangePhoneNumberTokenMessage" xml:space="preserve"> <value>Change phone number token has been sent to you</value> + </data> + <data name="SuccessfulSendTokenMessage" xml:space="preserve"> + <value>The token has been sent to you</value> + </data> + <data name="SessionsTitle" xml:space="preserve"> + <value>Sessions</value> + </data> + <data name="SessionsSubtitle" xml:space="preserve"> + <value>Your active sessions across multiple devices</value> </data> <data name="UserSessionsTitle" xml:space="preserve"> <value>Your sessions</value> @@ -222,27 +258,34 @@ </data> <data name="RemoveSessionSuccessMessage" xml:space="preserve"> <value>Session removed successfully</value> + </data> + <data name="NotFoundPageTitle" xml:space="preserve"> + <value>404</value> </data> <data name="NotFoundText" xml:space="preserve"> <value>There is nothing here.</value> </data> - <data name="UnknwonDevice" xml:space="preserve"> + <data name="UnknownDevice" xml:space="preserve"> <value>Unknown device</value> + </data> + <data name="UpdateWebViewThroughGooglePlay" xml:space="preserve"> + <value>Please update webview through google play.</value> + </data> + <data name="Ok" xml:space="preserve"> + <value>Ok</value> </data> <!--#if (offlineDb == true) --> <data name="OfflineEditProfileTitle" xml:space="preserve"> <value>Offline Edit profile</value> - </data> - <!--#endif--> - <!--#if (signalr == true) --> - <data name="TwoFactorTokenPushText" xml:space="preserve"> - <value>{0} is your two factor token in Boilerplate.</value> </data> <!--#endif--> <data name="FullName" xml:space="preserve"> <value>FullName</value> </data> - <data name="TermsTitle" xml:space="preserve"> + <data name="TermsPageTitle" xml:space="preserve"> + <value>Terms</value> + </data> + <data name="Terms" xml:space="preserve"> <value>Terms</value> </data> <data name="ProfileUpdatedSuccessfullyMessage" xml:space="preserve"> @@ -265,6 +308,9 @@ </data> <data name="NewEmailPlaceholder" xml:space="preserve"> <value>Enter new email</value> + </data> + <data name="Phone" xml:space="preserve"> + <value>Phone</value> </data> <data name="PhoneNumber" xml:space="preserve"> <value>Phone Number</value> @@ -301,9 +347,6 @@ </data> <data name="Error" xml:space="preserve"> <value>Error</value> - </data> - <data name="TermsAccepted" xml:space="preserve"> - <value>Is accept terms?</value> </data> <data name="YouHaveToAcceptTerms" xml:space="preserve"> <value>You must agree to our terms.</value> @@ -355,6 +398,18 @@ </data> <data name="Home" xml:space="preserve"> <value>Home</value> + </data> + <data name="Refresh" xml:space="preserve"> + <value>Refresh</value> + </data> + <data name="Recover" xml:space="preserve"> + <value>Recover</value> + </data> + <data name="SomethingWentWrong" xml:space="preserve"> + <value>Oops, something went wrong...</value> + </data> + <data name="BackToHome" xml:space="preserve"> + <value>Back to home</value> </data> <data name="UserLockedOut" xml:space="preserve"> <value>User is locked out. Try again in {0}</value> @@ -397,6 +452,9 @@ </data> <data name="ConfirmEmailSubtitle" xml:space="preserve"> <value>We have sent a confirmation token to your email address.</value> + </data> + <data name="ConfirmPageTitle" xml:space="preserve"> + <value>Confirm</value> </data> <data name="ConfirmTitle" xml:space="preserve"> <value>Confirm your account</value> @@ -443,7 +501,7 @@ <data name="PhoneTokenConfirmButtonText" xml:space="preserve"> <value>Confirm Phone Number</value> </data> - <data name="NotReceivedConfirmationPhoneMessage" xml:space="preserve"> + <data name="NotReceivedPhoneMessage" xml:space="preserve"> <value>Haven’t you received the token on your phone?</value> </data> <data name="ResendConfirmationPhoneTokenMessage" xml:space="preserve"> @@ -457,6 +515,12 @@ </data> <data name="PhoneConfirmationSuccessMessage" xml:space="preserve"> <value>You can now login with your account.</value> + </data> + <data name="AccountTitle" xml:space="preserve"> + <value>Account</value> + </data> + <data name="AccountSubtitle" xml:space="preserve"> + <value>Change your account email or phone number</value> </data> <data name="DeleteAccount" xml:space="preserve"> <value>Delete Account</value> @@ -482,7 +546,7 @@ <data name="FileUploadFailed" xml:space="preserve"> <value>An error occurred while uploading file</value> </data> - <data name="ForgotPassword" xml:space="preserve"> + <data name="ForgotPasswordPageTitle" xml:space="preserve"> <value>Forgot password</value> </data> <data name="ForgotPasswordTitle" xml:space="preserve"> @@ -506,8 +570,29 @@ <data name="HomeMessage" xml:space="preserve"> <value>Create your multi-mode (WASM, Server, Hybrid, pre-rendering) Blazor app easily in the shortest time ever!</value> </data> - <data name="HomeTitle" xml:space="preserve"> - <value>Boilerplate Home</value> + <data name="HomePageTitle" xml:space="preserve"> + <value>Home</value> + </data> + <data name="HomePanelTitle" xml:space="preserve"> + <value>Build all of your apps</value> + </data> + <data name="HomePanelSubtitle" xml:space="preserve"> + <value>using what you already know and love</value> + </data> + <data name="WatchVideo" xml:space="preserve"> + <value>Watch video</value> + </data> + <data name="LearnMore" xml:space="preserve"> + <value>Learn more</value> + </data> + <data name="BitPlatformMessage" xml:space="preserve"> + <value>A set of tools for .NET developers across multiple platforms</value> + </data> + <data name="BitBlazorUIMessage" xml:space="preserve"> + <value>A set of performant, easy-to-customize and beautiful Blazor components</value> + </data> + <data name="BitProjectTemplateMessage" xml:space="preserve"> + <value>A feature-rich .NET project template that works everywhere seamlessly</value> </data> <data name="DescriptionMetaTagValue" xml:space="preserve"> <value>The Boilerplate is built with ASP.NET Core, Identity, Web API, EF Core and Blazor.</value> @@ -524,11 +609,11 @@ <data name="No" xml:space="preserve"> <value>No</value> </data> - <data name="NotReceivedConfirmationEmailMessage" xml:space="preserve"> - <value>Haven’t you received the confirmation email?</value> + <data name="NotReceivedEmailMessage" xml:space="preserve"> + <value>Haven’t you received the email?</value> </data> <data name="Or" xml:space="preserve"> - <value>Or</value> + <value>or</value> </data> <data name="ProfileImage" xml:space="preserve"> <value>Profile Image</value> @@ -538,6 +623,12 @@ </data> <data name="ResendEmailTokenButtonText" xml:space="preserve"> <value>Resend Email Token</value> + </data> + <data name="ResetPasswordPageTitle" xml:space="preserve"> + <value>Reset password</value> + </data> + <data name="ResetPassword" xml:space="preserve"> + <value>Reset password</value> </data> <data name="ResetPasswordTitle" xml:space="preserve"> <value>Reset password</value> @@ -563,8 +654,17 @@ <data name="Save" xml:space="preserve"> <value>Save</value> </data> - <data name="SignInTitle" xml:space="preserve"> + <data name="SignInPageTitle" xml:space="preserve"> <value>Sign in</value> + </data> + <data name="SignInPanelTitle" xml:space="preserve"> + <value>Welcome back</value> + </data> + <data name="SignInPanelSubtitle" xml:space="preserve"> + <value>Sign in to your account with</value> + </data> + <data name="Continue" xml:space="preserve"> + <value>Continue</value> </data> <data name="SocialSignedInTitle" xml:space="preserve"> <value>Social signed in</value> @@ -602,8 +702,14 @@ <data name="SignUp" xml:space="preserve"> <value>Sign up</value> </data> - <data name="SingUpTitle" xml:space="preserve"> + <data name="SignUpPageTitle" xml:space="preserve"> <value>Sign up</value> + </data> + <data name="SignUpPanelTitle" xml:space="preserve"> + <value>Get started today</value> + </data> + <data name="SignUpPanelSubtitle" xml:space="preserve"> + <value>Create new account with</value> </data> <data name="Submit" xml:space="preserve"> <value>Submit</value> @@ -619,9 +725,6 @@ </data> <data name="SelectBirthDate" xml:space="preserve"> <value>Select your birth date</value> - </data> - <data name="ResetPassword" xml:space="preserve"> - <value>Reset password</value> </data> <data name="Action" xml:space="preserve"> <value>Action</value> @@ -667,12 +770,39 @@ </data> <data name="RememberMe" xml:space="preserve"> <value>Remember me?</value> + </data> + <data name="Copy" xml:space="preserve"> + <value>Copy</value> + </data> + <data name="Copied" xml:space="preserve"> + <value>Copied</value> + </data> + <data name="TfaTitle" xml:space="preserve"> + <value>Two-factor authentication</value> + </data> + <data name="TfaSubtitle" xml:space="preserve"> + <value>Manage your account's multi-factor authentication</value> </data> <data name="TwoFactorCode" xml:space="preserve"> - <value>Authenticator code</value> + <value>2FA code</value> </data> <data name="TwoFactorRecoveryCode" xml:space="preserve"> <value>Recovery code</value> + </data> + <data name="TfaPanelTitle" xml:space="preserve"> + <value>2-Factor Authentication</value> + </data> + <data name="TfaPanelSubtitle" xml:space="preserve"> + <value>Get a new code from your authenticator app or use your recovery code.</value> + </data> + <data name="TfaPanelAnotherWayTitle" xml:space="preserve"> + <value>Try another way</value> + </data> + <data name="TfaPanelAnotherWaySubtitle" xml:space="preserve"> + <value>You can get a new code at your email or phone.</value> + </data> + <data name="TfaPanelAnotherWayGetCode" xml:space="preserve"> + <value>Get code</value> </data> <data name="TwoFactorAuthTitle" xml:space="preserve"> <value>2FA</value> @@ -723,7 +853,7 @@ <value>Verify</value> </data> <data name="TfaRecoveryCodesHeader" xml:space="preserve"> - <value>Recovery codes</value> + <value>Recovery</value> </data> <data name="TfaRecoveryCodesZeroLeftTitle" xml:space="preserve"> <value>You have no recovery codes left.</value> @@ -759,7 +889,7 @@ <value>Recovery codes:</value> </data> <data name="TfaAuthAppHeader" xml:space="preserve"> - <value>Authenticator app</value> + <value>Authenticator</value> </data> <data name="TfaAuthAppWarningTitle" xml:space="preserve"> <value>If you reset your authenticator key your authenticator app will not work until you reconfigure it.</value> @@ -771,7 +901,7 @@ <value>Reset authenticator key</value> </data> <data name="TfaDisable2faHeader" xml:space="preserve"> - <value>Disable 2FA</value> + <value>Disable</value> </data> <data name="TfaDisable2faWarningTitle" xml:space="preserve"> <value>This action only disables 2FA.</value> @@ -808,6 +938,21 @@ </data> <data name="Otp" xml:space="preserve"> <value>OTP</value> + </data> + <data name="OtpPanelTitle" xml:space="preserve"> + <value>Check your {0}</value> + </data> + <data name="OtpPanelSubtitle" xml:space="preserve"> + <value>Confirm your {0} by entering the code sent to {1}</value> + </data> + <data name="Code" xml:space="preserve"> + <value>Code</value> + </data> + <data name="OtpResendMessage" xml:space="preserve"> + <value>Didn't get the code?</value> + </data> + <data name="Resend" xml:space="preserve"> + <value>Resend</value> </data> <data name="OtpPlaceholder" xml:space="preserve"> <value>Enter OTP</value> @@ -833,13 +978,13 @@ <data name="ConfirmPhoneTokenSmsText" xml:space="preserve"> <value>{0} is your confirm phone number token in Boilerplate.</value> </data> - <data name="TwoFactorTokenSmsText" xml:space="preserve"> + <data name="TwoFactorTokenShortText" xml:space="preserve"> <value>{0} is your two factor token in Boilerplate.</value> </data> - <data name="OtpSmsText" xml:space="preserve"> + <data name="OtpShortText" xml:space="preserve"> <value>{0} is your OTP in Boilerplate.</value> </data> - <data name="ResetPasswordTokenSmsText" xml:space="preserve"> + <data name="ResetPasswordTokenShortText" xml:space="preserve"> <value>{0} is your reset password token in Boilerplate.</value> </data> <data name="Online" xml:space="preserve"> @@ -862,6 +1007,9 @@ </data> <data name="AboutTitle" xml:space="preserve"> <value>About</value> + </data> + <data name="NothingFound" xml:space="preserve"> + <value>Nothing found!</value> </data> <!--#if (sample == "Todo") --> <data name="TodoAddPlaceholder" xml:space="preserve"> @@ -871,18 +1019,24 @@ <value>No todos yet</value> </data> <data name="TodoSearchPlaceholder" xml:space="preserve"> - <value>Search some todo...</value> + <value>Search todo...</value> </data> <data name="ToDoItemCouldNotBeFound" xml:space="preserve"> <value>Todo item could not be found</value> </data> - <data name="TodoTitle" xml:space="preserve"> + <data name="TodoPageTitle" xml:space="preserve"> + <value>Todo</value> + </data> + <data name="Todo" xml:space="preserve"> <value>Todo</value> </data> <data name="DeleteTodoItem" xml:space="preserve"> <value>Delete todo item</value> </data> <!--#elif (sample == "Admin") --> + <data name="AdminPanel" xml:space="preserve"> + <value>Admin panel</value> + </data> <data name="ProductsCount" xml:space="preserve"> <value>Products count</value> </data> @@ -916,8 +1070,8 @@ <data name="EnterProductName" xml:space="preserve"> <value>Enter product name</value> </data> - <data name="Last30DaysCategoryCount" xml:space="preserve"> - <value>Last 30 days category count</value> + <data name="CategoriesWithProductCount" xml:space="preserve"> + <value>Categories with product count</value> </data> <data name="ProductSales" xml:space="preserve"> <value>Product sales</value> @@ -981,9 +1135,15 @@ </data> <data name="EditProduct" xml:space="preserve"> <value>Edit product</value> + </data> + <data name="DashboardPageTitle" xml:space="preserve"> + <value>Dashboard</value> </data> <data name="Dashboard" xml:space="preserve"> <value>Dashboard</value> + </data> + <data name="DashboardSubtitle" xml:space="preserve"> + <value>Here's your analytic data</value> </data> <!--#endif --> <!--#if (captcha == "reCaptcha") --> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.fr.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.nl.resx similarity index 84% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.fr.resx rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.nl.resx index d4c7afc0d5..2dba1f7e35 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.fr.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/IdentityStrings.nl.resx @@ -119,91 +119,91 @@ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="ConcurrencyFailure" xml:space="preserve"> - <value>Échec de concurrence optimiste, l'objet a été modifié.</value> + <value>Optimistische gelijktijdigheidsmislukking, object is gewijzigd.</value> <comment>Error when optimistic concurrency fails</comment> </data> <data name="DefaultError" xml:space="preserve"> - <value>Une panne inconnue s'est produite.</value> + <value>Er is een onbekende storing opgetreden.</value> <comment>Default identity result error message</comment> </data> <data name="DuplicateEmail" xml:space="preserve"> - <value>L'e-mail « {0} » est déjà pris.</value> + <value>E-mail '{0}' is al in gebruik.</value> <comment>Error for duplicate emails</comment> </data> <data name="DuplicateRoleName" xml:space="preserve"> - <value>Le nom de rôle « {0} » est déjà pris.</value> + <value>Rolnaam '{0}' is al bezet.</value> <comment>Error for duplicate roles</comment> </data> <data name="DuplicateUserName" xml:space="preserve"> - <value>Le nom d'utilisateur '{0}' est déjà pris.</value> + <value>Gebruikersnaam '{0}' is al in gebruik.</value> <comment>Error for duplicate user names</comment> </data> <data name="InvalidEmail" xml:space="preserve"> - <value>L'e-mail « {0} » n'est pas valide.</value> + <value>E-mail '{0}' is ongeldig.</value> <comment>Invalid email</comment> </data> <data name="InvalidRoleName" xml:space="preserve"> - <value>Le nom de rôle « {0} » n'est pas valide.</value> + <value>De rolnaam '{0}' is ongeldig.</value> <comment>Error for invalid role names</comment> </data> <data name="InvalidToken" xml:space="preserve"> - <value>Jeton invalide.</value> + <value>Ongeldig token.</value> <comment>Error when a token is not recognized</comment> </data> <data name="InvalidUserName" xml:space="preserve"> - <value>Le nom d'utilisateur '{0}' n'est pas valide et ne peut contenir que des lettres ou des chiffres.</value> + <value>Gebruikersnaam '{0}' is ongeldig, kan alleen letters of cijfers bevatten.</value> <comment>User names can only contain letters or digits</comment> </data> <data name="LoginAlreadyAssociated" xml:space="preserve"> - <value>Un utilisateur avec cette connexion existe déjà.</value> + <value>Er bestaat al een gebruiker met deze login.</value> <comment>Error when a login already linked</comment> </data> <data name="PasswordMismatch" xml:space="preserve"> - <value>Mot de passe incorrect.</value> + <value>Onjuist wachtwoord.</value> <comment>Error when a password doesn't match</comment> </data> <data name="PasswordRequiresDigit" xml:space="preserve"> - <value>Les mots de passe doivent comporter au moins un chiffre (« 0 » - « 9 »).</value> + <value>Wachtwoorden moeten uit ten minste één cijfer ('0'-'9') bestaan.</value> <comment>Error when passwords do not have a digit</comment> </data> <data name="PasswordRequiresLower" xml:space="preserve"> - <value>Les mots de passe doivent comporter au moins une minuscule (« a » - « z »).</value> + <value>Wachtwoorden moeten ten minste één kleine letter ('a'-'z') bevatten.</value> <comment>Error when passwords do not have a lowercase letter</comment> </data> <data name="PasswordRequiresNonAlphanumeric" xml:space="preserve"> - <value>Les mots de passe doivent comporter au moins un caractère non alphanumérique.</value> + <value>Wachtwoorden moeten ten minste één niet-alfanumeriek teken bevatten.</value> <comment>Error when password does not have enough non alphanumeric characters</comment> </data> <data name="PasswordRequiresUpper" xml:space="preserve"> - <value>Les mots de passe doivent comporter au moins une majuscule (« A » - « Z »).</value> + <value>Wachtwoorden moeten ten minste één hoofdletter ('A'-'Z') bevatten.</value> <comment>Error when passwords do not have an uppercase letter</comment> </data> <data name="PasswordTooShort" xml:space="preserve"> - <value>Les mots de passe doivent comporter au moins {0} caractères.</value> + <value>Wachtwoorden moeten minimaal {0} tekens lang zijn.</value> <comment>Error message for passwords that are too short</comment> </data> <data name="RecoveryCodeRedemptionFailed" xml:space="preserve"> - <value>L'utilisation du code de récupération a échoué.</value> + <value>Het inwisselen van de herstelcode is mislukt.</value> <comment>Error when a recovery code is not redeemed.</comment> </data> <data name="UserAlreadyHasPassword" xml:space="preserve"> - <value>L'utilisateur a déjà un mot de passe défini.</value> + <value>De gebruiker heeft al een wachtwoord ingesteld.</value> <comment>Error when AddPasswordAsync called when a user already has a password</comment> </data> <data name="UserAlreadyInRole" xml:space="preserve"> - <value>Utilisateur déjà dans le rôle « {0} ».</value> + <value>Gebruiker al in rol '{0}'.</value> <comment>Error when a user is already in a role</comment> </data> <data name="UserLockoutNotEnabled" xml:space="preserve"> - <value>Le verrouillage n'est pas activé pour cet utilisateur.</value> + <value>Vergrendeling is niet ingeschakeld voor deze gebruiker.</value> <comment>Error when lockout is not enabled</comment> </data> <data name="UserNotInRole" xml:space="preserve"> - <value>L'utilisateur n'a pas le rôle « {0} ».</value> + <value>Gebruiker heeft niet de rol '{0}'.</value> <comment>Error when a user is not in the role</comment> </data> <data name="PasswordRequiresUniqueChars" xml:space="preserve"> - <value>Les mots de passe doivent utiliser au moins {0} caractères différents.</value> + <value>Wachtwoorden moeten ten minste {0} verschillende tekens bevatten.</value> <comment>Error message for passwords that are based on similar characters</comment> </data> </root> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs index 712c1e98ae..3333d1ea2d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Shared.Services.Contracts; /// (explained in this documentation: https://docs.microsoft.com/en-us/aspnet/core/blazor/components/prerendering-and-integration#persist-prerendered-state). /// If your project does not require prerendering to be enabled, you can completely remove this service and its usages from your project. /// </summary> -public interface IPrerenderStateService +public interface IPrerenderStateService : IAsyncDisposable { /// <summary> /// Instead of using ApplicationState.TryTakeFromJson, ApplicationState.RegisterOnPersisting, diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/CultureInfoManager.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/CultureInfoManager.cs index 505e882b24..e249529b1d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/CultureInfoManager.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/CultureInfoManager.cs @@ -20,8 +20,9 @@ public static (string DisplayName, CultureInfo Culture)[] SupportedCultures => [ ("English US", CreateCultureInfo("en-US")), ("English UK", CreateCultureInfo("en-GB")), - ("Française", CreateCultureInfo("fr-FR")), + ("Dutch", CreateCultureInfo("nl-NL")), ("فارسی", CreateCultureInfo("fa-IR")) + // Adding new cultures requires changing MainActivity's DataPathPrefixes. ]; public static CultureInfo CreateCultureInfo(string name) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs new file mode 100644 index 0000000000..d285be7803 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs @@ -0,0 +1,29 @@ +//+:cnd:noEmit +namespace Boilerplate.Shared.Services; + +/// <summary> +/// This class is located in the Shared project to defines +/// message keys used for pub/sub messaging between server and client through SignalR. +/// For client-only pub/sub messages, refer to the ClientPubSubMessages class in the Client/Core project. +/// </summary> +public static partial class SharedPubSubMessages +{ + //#if (sample == "Admin") + public const string DASHBOARD_DATA_CHANGED = nameof(DASHBOARD_DATA_CHANGED); + //#else + // To see examples of this class, checkout the admin panel sample. + //#endif +} + +public static partial class SignalREvents +{ + /// <summary> + /// Sends a message to the clients that will published through PubSubService. + /// </summary> + public const string PUBLISH_MESSAGE = nameof(PUBLISH_MESSAGE); + + /// <summary> + /// Shows message at client side. + /// </summary> + public const string SHOW_MESSAGE = nameof(SHOW_MESSAGE); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/SharedSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/SharedSettings.cs new file mode 100644 index 0000000000..f2f9c80e7c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/SharedSettings.cs @@ -0,0 +1,35 @@ +//+:cnd:noEmit +namespace Boilerplate.Shared; + +public partial class SharedSettings : IValidatableObject +{ + /// <summary> + /// If you are hosting the API and web client on different URLs (e.g., api.company.com and app.company.com), you must set `WebClientUrl` to your web client's address. + /// This ensures that the API server redirects to the correct URL after social sign-ins and other similar actions. + /// </summary> + public string? WebClientUrl { get; set; } + + //#if (appInsights == true) + public ApplicationInsightsOptions? ApplicationInsights { get; set; } + //#endif + + public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) + { + var validationResults = new List<ValidationResult>(); + + //#if (appInsights == true) + if (ApplicationInsights is not null) + { + Validator.TryValidateObject(ApplicationInsights, new ValidationContext(ApplicationInsights), validationResults, true); + } + //#endif + + return validationResults; + } +} + +//#if (appInsights == true) +public class ApplicationInsightsOptions +{ + public string? ConnectionString { get; set; } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.Identity.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.Identity.cs new file mode 100644 index 0000000000..61e94cefbb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.Identity.cs @@ -0,0 +1,21 @@ +//+:cnd:noEmit +namespace Boilerplate.Shared; + +public static partial class Urls +{ + public const string NotAuthorizedPage = "/not-authorized"; + + public const string ConfirmPage = "/confirm"; + + public const string ForgotPasswordPage = "/forgot-password"; + + public const string ResetPasswordPage = "/reset-password"; + + public const string SignInPage = "/sign-in"; + + public const string SignUpPage = "/sign-up"; + + //#if (offlineDb == true) + public const string OfflineEditProfilePage = "/offline-edit-profile"; + //#endif +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.SettingsSections.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.SettingsSections.cs new file mode 100644 index 0000000000..fab5af5403 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.SettingsSections.cs @@ -0,0 +1,12 @@ +namespace Boilerplate.Shared; + +public static partial class Urls +{ + public static class SettingsSections + { + public static readonly string Profile = nameof(Profile).ToLower(); + public static readonly string Account = nameof(Account).ToLower(); + public static readonly string Tfa = nameof(Tfa).ToLower(); + public static readonly string Sessions = nameof(Sessions).ToLower(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.cs index 8f8d58d070..a72a789d61 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Urls.cs @@ -5,23 +5,13 @@ public static partial class Urls { public const string HomePage = "/"; - public const string NotAuthorizedPage = "/not-authorized"; - public const string NotFoundPage = "/not-found"; public const string TermsPage = "/terms"; - public const string ProfilePage = "/profile"; - - public const string ConfirmPage = "/confirm"; - - public const string ForgotPasswordPage = "/forgot-password"; - - public const string ResetPasswordPage = "/reset-password"; - - public const string SignInPage = "/sign-in"; + public const string SettingsPage = "/settings"; - public const string SignUpPage = "/sign-up"; + public const string AboutPage = "/about"; //#if (sample == "Admin") public const string AddOrEditCategoryPage = "/add-edit-category"; @@ -36,9 +26,8 @@ public static partial class Urls public const string TodoPage = "/todo"; //#endif - //#if (offlineDb == true) - public const string OfflineEditProfilePage = "/offline-edit-profile"; - //#endif - - public const string AboutPage = "/about"; + public static readonly string[] All = typeof(Urls).GetFields() + .Where(f => f.FieldType == typeof(string)) + .Select(f => f.GetValue(null)!.ToString()!) + .ToArray(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Development.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Development.json index 7a623fb31e..d2a86e4696 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Development.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Development.json @@ -1,21 +1,65 @@ { "Logging": { - "LogLevel": { - "Default": "Information" + "Console": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true }, - //#if (appCenter == true) - "AppCenterLoggerProvider": { + "EventLog": { "LogLevel": { - "Default": "Information" - } + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true + }, + "EventSource": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true + }, + "DiagnosticLogger": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true + }, + "Debug": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true }, - //#endif //#if (appInsights == true) "ApplicationInsights": { "LogLevel": { - "Default": "Information" - } + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": false + }, + "ApplicationInsightsLoggerProvider": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": false + }, + //#endif + //#if (appCenter == true) + "AppCenterLoggerProvider": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": false } //#endif - } + }, + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Production.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Production.json index 544b7b4ddd..2b7a979354 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Production.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.Production.json @@ -1,3 +1,3 @@ { - + "$schema": "https://json.schemastore.org/appsettings.json" } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.json index 79f45d7e26..f7313af1be 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/appsettings.json @@ -1,35 +1,74 @@ { + "WebClientUrl": null, + "WebClientUrl_Comment": "If you are hosting the API and web client on different URLs (e.g., api.company.com and app.company.com), you must set `WebClientUrl` to your web client's address. This ensures that the API server redirects to the correct URL after social sign-ins and other similar actions.", + //#if (appInsights == true) + "ApplicationInsights": { + "ConnectionString": null + }, + //#endif "Logging": { - "LogLevel": { - "Default": "Warning", - "Boilerplate.Client.Core.Services.AuthenticationManager": "Information", - "Microsoft.EntityFrameworkCore.Database.Command": "Information" + //#if (appInsights == true) + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information", + "Boilerplate.Client.Core.Services.AuthenticationManager": "Information" + }, + "IncludeScopes": true }, + "ApplicationInsightsLoggerProvider": { + "LogLevel": { + "Default": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information", + "Boilerplate.Client.Core.Services.AuthenticationManager": "Information" + }, + "IncludeScopes": true + }, + //#endif //#if (appCenter == true) "AppCenterLoggerProvider": { "LogLevel": { "Default": "Warning", - "Boilerplate.Client.Core.Services.AuthenticationManager": "Information", - "Microsoft.EntityFrameworkCore.Database.Command": "Information" - } + "Microsoft.EntityFrameworkCore.Database.Command": "Information", + "Boilerplate.Client.Core.Services.AuthenticationManager": "Information" + }, + "IncludeScopes": true }, //#endif - //#if (appInsights == true) - "ApplicationInsights": { + "Console": { + "LogLevel": { + "Default": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + }, + "IncludeScopes": true + }, + "EventLog": { "LogLevel": { "Default": "Warning", - "Boilerplate.Client.Core.Services.AuthenticationManager": "Information", "Microsoft.EntityFrameworkCore.Database.Command": "Information" - } + }, + "IncludeScopes": true + }, + "EventSource": { + "LogLevel": { + "Default": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + }, + "IncludeScopes": true + }, + "DiagnosticLogger": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "IncludeScopes": true + }, + "Debug": { + "LogLevel": { + "Default": "None" + }, + "IncludeScopes": true } - //#endif }, - "WebClientUrl": null, - "WebClientUrl_Comment": "If you are hosting the API and web client on different URLs (e.g., api.company.com and app.company.com), you must set `WebClientUrl` to your web client's address. This ensures that the API server redirects to the correct URL after social sign-ins and other similar actions.", - //#if (appInsights == true) - "ApplicationInsights": { - "ConnectionString": "" - }, - //#endif "$schema": "https://json.schemastore.org/appsettings.json" } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings new file mode 100644 index 0000000000..0948247807 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +:cnd:noEmit --> +<RunSettings> + <Playwright> + <LaunchOptions> + <!--<Headless>true</Headless>--> + <!--<SlowMo>1000</SlowMo>--> + </LaunchOptions> + <!--<BrowserName>chromium</BrowserName>--> + </Playwright> + + <MSTest> + <Parallelize> + <Workers>3</Workers> + <Scope>MethodLevel</Scope> + </Parallelize> + </MSTest> + + <RunConfiguration> + <CollectSourceInformation>true</CollectSourceInformation> + <EnvironmentVariables> + <!-- https://playwright.dev/docs/debug --> + <!--<HEADED>1</HEADED>--> + <!--<PWDEBUG>1</PWDEBUG>--> + </EnvironmentVariables> + </RunConfiguration> + + <!-- https://learn.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file --> + <LoggerRunSettings> + <Loggers> + <Logger friendlyName="html" enabled="True"> + <Configuration> + <LogFileName>TestResults.html</LogFileName> + </Configuration> + </Logger> + <Logger friendlyName="trx" enabled="True"> + <Configuration> + <LogFileName>TestResults.trx</LogFileName> + </Configuration> + </Logger> + </Loggers> + </LoggerRunSettings> +</RunSettings> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/AppTestServer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/AppTestServer.cs index a8ffeceb43..d6b0252066 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/AppTestServer.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/AppTestServer.cs @@ -1,86 +1,77 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; -using Microsoft.AspNetCore.TestHost; +using System.Net.Sockets; +using System.Net; using Boilerplate.Server.Api; -using Boilerplate.Server.Api.Data; +using Boilerplate.Server.Web; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace Boilerplate.Tests; public partial class AppTestServer : IAsyncDisposable { - private TestServer? testServer; private WebApplication? webApp; - public AppTestServer Build(Action<IServiceCollection>? configureTestServices = null) + public WebApplication WebApp => webApp ?? throw new InvalidOperationException($"{nameof(WebApp)} is null. Call {nameof(Build)} method first."); + public readonly Uri WebAppServerAddress = new(GenerateServerUrl()); + + public AppTestServer Build(Action<IServiceCollection>? configureTestServices = null, + Action<ConfigurationManager>? configureTestConfigurations = null) { if (webApp != null) throw new InvalidOperationException("Server is already built."); var builder = WebApplication.CreateBuilder(options: new() { - EnvironmentName = Environments.Development + EnvironmentName = Environments.Development, + ApplicationName = typeof(Server.Web.Program).Assembly.GetName().Name }); + builder.Configuration["ServerAddress"] = WebAppServerAddress.ToString(); + builder.WebHost.UseUrls(WebAppServerAddress.ToString()); + AppEnvironment.Set(builder.Environment.EnvironmentName); - builder.Configuration.AddSharedConfigurations(); + builder.Configuration.AddClientConfigurations(clientEntryAssemblyName: "Boilerplate.Client.Web"); - builder.WebHost.UseTestServer(); + //#if (database == 'Sqlite') + //Use in-memory Sqlite database for faster and more reliable testing + builder.Configuration["ConnectionStrings:SqliteConnectionString"] = "Data Source=BoilerplateDb;Mode=Memory;Cache=Shared;"; + //#endif + + configureTestConfigurations?.Invoke(builder.Configuration); + + builder.AddTestProjectServices(); configureTestServices?.Invoke(builder.Services); - builder.ConfigureApiServices(); - builder.Services.AddSharedProjectServices(); var app = webApp = builder.Build(); - app.ConfiureMiddlewares(); + app.ConfigureMiddlewares(); return this; } public async Task Start() { - if (webApp == null) - throw new InvalidOperationException($"Call {nameof(Build)} first."); - - if (AppEnvironment.IsDev()) - { - await using var scope = webApp.Services.CreateAsyncScope(); - var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); - await dbContext.Database.EnsureCreatedAsync(); - } - - await webApp.StartAsync(); - - testServer = webApp.GetTestServer(); - } - - public HttpClient CreateClient() - { - return (testServer ?? throw new InvalidOperationException()).CreateClient(); - } - - public HttpMessageHandler CreateHandler() - { - return (testServer ?? throw new InvalidOperationException()).CreateHandler(); - } - - public RequestBuilder CreateRequest(string path) - { - return (testServer ?? throw new InvalidOperationException()).CreateRequest(path); - } - - public WebSocketClient CreateWebSocketClient() - { - return (testServer ?? throw new InvalidOperationException()).CreateWebSocketClient(); + await WebApp.StartAsync(); } public async ValueTask DisposeAsync() { if (webApp != null) { + await webApp.StopAsync(); await webApp.DisposeAsync(); } - testServer?.Dispose(); + } + + private static string GenerateServerUrl() + { + using var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + listener.Stop(); + return $"http://127.0.0.1:{port}/"; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj index 39dba441c6..2c299476b7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj @@ -2,34 +2,49 @@ <PropertyGroup> <TargetFramework>net8.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> - <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> + <RunSettingsFilePath>$(MSBuildProjectDirectory)\.runsettings</RunSettingsFilePath> </PropertyGroup> <ItemGroup> - <PackageReference Include="coverlet.collector" Version="6.0.2"> + <PackageReference Include="coverlet.collector"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="FakeItEasy" Version="8.3.0" /> - <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" /> - <PackageReference Include="MSTest.TestAdapter" Version="3.5.2" /> - <PackageReference Include="MSTest.TestFramework" Version="3.5.2" /> + <PackageReference Include="FakeItEasy" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Microsoft.Playwright.MSTest" /> + <PackageReference Include="MSTest.TestAdapter" /> + <PackageReference Include="MSTest.TestFramework" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\Server\Boilerplate.Server.Web\Boilerplate.Server.Web.csproj" /> <ProjectReference Include="..\Server\Boilerplate.Server.Api\Boilerplate.Server.Api.csproj" /> </ItemGroup> <ItemGroup> <Using Include="System.Net.Http.Headers" /> <Using Include="System.Net.Http.Json" /> + <Using Include="Boilerplate.Shared" /> <Using Include="Boilerplate.Shared.Enums" /> + <Using Include="Microsoft.AspNetCore.Http" /> <Using Include="Microsoft.AspNetCore.Components" /> <Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" /> + <Using Include="Microsoft.Playwright" /> + <Using Include="Microsoft.Playwright.MSTest" /> + <Using Include="FakeItEasy" /> + <!--/+:msbuild-conditional:noEmit --> + <PackageReference Condition=" '$(pipeline)' == 'GitHub' OR '$(pipeline)' == ''" Include="GitHubActionsTestLogger" PrivateAssets="all" /> + <PackageReference Condition=" '$(pipeline)' == 'Azure' " Include="AzurePipelines.TestLogger" /> + <!--/-:msbuild-conditional:noEmit --> </ItemGroup> + <!--/+:msbuild-conditional:noEmit --> + <ItemGroup Condition=" '$(advancedTests)' == 'true' OR '$(advancedTests)' == ''"> + <PackageReference Include="MsgReader" /> + </ItemGroup> + <!--/-:msbuild-conditional:noEmit --> + </Project> diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightCacheExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightCacheExtensions.cs new file mode 100644 index 0000000000..2d42b91d73 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightCacheExtensions.cs @@ -0,0 +1,52 @@ +using System.Collections.Concurrent; +using System.Text.RegularExpressions; + +namespace Boilerplate.Tests.Extensions; + +public static partial class PlaywrightCacheExtensions +{ + private static readonly ConcurrentDictionary<string, (byte[] Body, Dictionary<string, string> Headers)> cachedResponses = []; + + public static Task EnableBlazorWasmCaching(this IPage page) => page.EnableAssetCaching(BlazorWasmRegex()); + + public static Task EnableBlazorWasmCaching(this IBrowserContext context) => context.EnableAssetCaching(BlazorWasmRegex()); + + public static Task EnableAssetCaching(this IPage page, string routePattern) => page.RouteAsync(routePattern, CacheHandler); + + public static Task EnableAssetCaching(this IPage page, Regex routePattern) => page.RouteAsync(routePattern, CacheHandler); + + public static Task EnableAssetCaching(this IPage page, Func<string, bool> predicate) => page.RouteAsync(predicate, CacheHandler); + + public static Task EnableAssetCaching(this IBrowserContext context, string routePattern) => context.RouteAsync(routePattern, CacheHandler); + + public static Task EnableAssetCaching(this IBrowserContext context, Regex routePattern) => context.RouteAsync(routePattern, CacheHandler); + + public static Task EnableAssetCaching(this IBrowserContext context, Func<string, bool> predicate) => context.RouteAsync(predicate, CacheHandler); + + private static async Task CacheHandler(IRoute route) + { + var url = new Uri(route.Request.Url).PathAndQuery; + + if (cachedResponses.TryGetValue(url, out var cachedResponse) is false) + { + var response = await route.FetchAsync(); + var body = await response.BodyAsync(); + cachedResponse = (body, response.Headers); + cachedResponses[url] = cachedResponse; + } + + await route.FulfillAsync(new RouteFulfillOptions + { + Status = 200, + BodyBytes = cachedResponse.Body, + Headers = cachedResponse.Headers + }); + } + + public static void ClearCache() => cachedResponses.Clear(); + + public static bool ContainsAsset(Regex regex) => cachedResponses.Keys.Any(regex.IsMatch); + + [GeneratedRegex(@"\/_framework\/[\w\.]+\.((wasm)|(pdb)|(dat))\?v=sha256-.+")] + private static partial Regex BlazorWasmRegex(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightVideoRecordingExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightVideoRecordingExtensions.cs new file mode 100644 index 0000000000..3f9c4c25da --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/PlaywrightVideoRecordingExtensions.cs @@ -0,0 +1,67 @@ +//+:cnd:noEmit +using System.Reflection; + +namespace Boilerplate.Tests.Extensions; + +public static class PlaywrightVideoRecordingExtensions +{ + public static async Task FinalizeVideoRecording(this PageTest page) + { + await FinalizeVideoRecording(page.Context, page.TestContext); + } + + public static async Task FinalizeVideoRecording(this IBrowserContext browserContext, TestContext testContext) + { + await browserContext.CloseAsync(); + if (testContext.CurrentTestOutcome is not UnitTestOutcome.Failed) + { + var directory = GetVideoDirectory(testContext); + if (Directory.Exists(directory)) + Directory.Delete(directory, true); + } + } + + public static BrowserNewContextOptions EnableVideoRecording(this BrowserNewContextOptions options, TestContext testContext) + { + options.RecordVideoDir = GetVideoDirectory(testContext); + return options; + } + + public static BrowserNewContextOptions EnableVideoRecording(this BrowserNewContextOptions options, TestContext testContext, string testMethodFullName) + { + options.RecordVideoDir = GetVideoDirectory(testContext, testMethodFullName); + return options; + } + + private static string GetVideoDirectory(TestContext testContext) + { + var testMethodFullName = $"{testContext.FullyQualifiedTestClassName}.{GetTestMethodName(testContext)}"; + return GetVideoDirectory(testContext, testMethodFullName); + } + + private static string GetVideoDirectory(TestContext testContext, string testMethodFullName) + { + // Remove invalid characters from the test method name + char[] notAllowedChars = [')', '"', '<', '>', '|', '*', '?', '\r', '\n', .. Path.GetInvalidFileNameChars()]; + testMethodFullName = new string(testMethodFullName.Where(ch => !notAllowedChars.Contains(ch)).ToArray()).Replace('(', '_').Replace(',', '_'); + + var dir = Path.Combine(testContext.TestResultsDirectory!, "..", "..", "Videos", testMethodFullName); + return Path.GetFullPath(dir); + } + + private static string GetTestMethodName(TestContext testContext) + { + //#if (advancedTests == true) + //Extract display name instead of method name + var testContextType = testContext.GetType(); + var testMethodField = testContextType.GetField("_testMethod", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("Field '_testMethod' not found."); + var testMethod = testMethodField.GetValue(testContext) ?? throw new InvalidOperationException("Field '_testMethod' is null."); + var testMethodType = testMethod.GetType(); + var displayNameProperty = testMethodType.GetProperty("DisplayName", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("Property 'DisplayName' not found."); + var displayName = displayNameProperty.GetValue(testMethod) ?? throw new InvalidOperationException("Field 'DisplayName' is null."); + return (string)displayName; + //#else + return testContext.TestName!; + //#endif + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/WebApplicationBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/WebApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..82f5de38de --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Extensions/WebApplicationBuilderExtensions.cs @@ -0,0 +1,31 @@ +//+:cnd:noEmit +using Boilerplate.Server.Web; +using Boilerplate.Tests.Services; +using Boilerplate.Server.Api.Services; + +namespace Microsoft.AspNetCore.Builder; + +public static partial class WebApplicationBuilderExtensions +{ + public static void AddTestProjectServices(this WebApplicationBuilder builder) + { + var services = builder.Services; + + builder.AddServerWebProjectServices(); + + //#if (advancedTests == true) + services.AddTransient<PhoneService, FakePhoneService>(); + //#if (captcha == "reCaptcha") + services.AddTransient<GoogleRecaptchaHttpClient, FakeGoogleRecaptchaHttpClient>(); + //#endif + //#endif + + services.AddTransient(sp => + { + return new HttpClient(sp.GetRequiredService<HttpMessageHandler>()) + { + BaseAddress = new Uri(sp.GetRequiredService<IConfiguration>().GetServerAddress(), UriKind.Absolute) + }; + }); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityApiTests.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityApiTests.cs new file mode 100644 index 0000000000..dde2251bdb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityApiTests.cs @@ -0,0 +1,57 @@ +using Boilerplate.Client.Core.Services; +using Boilerplate.Client.Core.Services.Contracts; +using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Tests.Services; + +namespace Boilerplate.Tests; + +[TestClass] +public partial class IdentityApiTests +{ + [TestMethod] + public async Task SignInTest() + { + await using var server = new AppTestServer(); + + await server.Build(services => + { + // Services registered in this test project will be used instead of the application's services, allowing you to fake certain behaviors during testing. + services.Replace(ServiceDescriptor.Scoped<IStorageService, TestStorageService>()); + services.Replace(ServiceDescriptor.Transient<IAuthTokenProvider, TestAuthTokenProvider>()); + }).Start(); + + await using var scope = server.WebApp.Services.CreateAsyncScope(); + + var authenticationManager = scope.ServiceProvider.GetRequiredService<AuthenticationManager>(); + + await authenticationManager.SignIn(new() + { + Email = TestData.DefaultTestEmail, + Password = TestData.DefaultTestPassword + }, default); + + var userController = scope.ServiceProvider.GetRequiredService<IUserController>(); + + var user = await userController.GetCurrentUser(default); + + Assert.AreEqual(Guid.Parse("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), user.Id); + } + + [TestMethod] + public async Task UnauthorizedAccessTest() + { + await using var server = new AppTestServer(); + + await server.Build(services => + { + services.Replace(ServiceDescriptor.Scoped<IStorageService, TestStorageService>()); + services.Replace(ServiceDescriptor.Transient<IAuthTokenProvider, TestAuthTokenProvider>()); + }).Start(); + + await using var scope = server.WebApp.Services.CreateAsyncScope(); + + var userController = scope.ServiceProvider.GetRequiredService<IUserController>(); + + await Assert.ThrowsExceptionAsync<UnauthorizedException>(() => userController.GetCurrentUser(default)); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityPagesTests.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityPagesTests.cs new file mode 100644 index 0000000000..c1aa75e2b8 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/IdentityPagesTests.cs @@ -0,0 +1,53 @@ +using Boilerplate.Tests.Extensions; + +namespace Boilerplate.Tests; + +[TestClass] +public partial class IdentityPagesTests : PageTest +{ + [TestMethod] + public async Task UnauthorizedUser_Should_RedirectToSignInPage() + { + await using var server = new AppTestServer(); + + await server.Build(services => + { + // Services registered in this test project will be used instead of the application's services, allowing you to fake certain behaviors during testing. + }).Start(); + + await Page.GotoAsync(new Uri(server.WebAppServerAddress, Urls.SettingsPage).ToString()); + + await Expect(Page).ToHaveURLAsync(new Uri(server.WebAppServerAddress, "/sign-in?return-url=settings").ToString()); + } + + [TestMethod] + public async Task SignIn_Should_WorkAsExpected() + { + await using var server = new AppTestServer(); + await server.Build().Start(); + + await Page.GotoAsync(new Uri(server.WebAppServerAddress, Urls.SignInPage).ToString()); + + await Expect(Page).ToHaveTitleAsync(AppStrings.SignInPageTitle); + + const string email = TestData.DefaultTestEmail; + const string password = TestData.DefaultTestPassword; + const string userFullName = TestData.DefaultTestFullName; + + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(email); + await Page.GetByPlaceholder(AppStrings.PasswordPlaceholder).FillAsync(password); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn }).ClickAsync(); + + await Expect(Page).ToHaveURLAsync(server.WebAppServerAddress.ToString()); + await Expect(Page.GetByRole(AriaRole.Button, new() { Name = userFullName })).ToBeVisibleAsync(); + await Expect(Page.Locator(".bit-prs.persona").First).ToContainTextAsync(userFullName); + await Expect(Page.Locator(".bit-prs.persona").Last).ToContainTextAsync(userFullName); + await Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut })).ToBeVisibleAsync(); + await Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn })).ToBeHiddenAsync(); + } + + public override BrowserNewContextOptions ContextOptions() => base.ContextOptions().EnableVideoRecording(TestContext); + + [TestCleanup] + public async ValueTask Cleanup() => await this.FinalizeVideoRecording(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/BlazorWebAssemblyTests.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/BlazorWebAssemblyTests.cs new file mode 100644 index 0000000000..b39c56c77e --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/BlazorWebAssemblyTests.cs @@ -0,0 +1,29 @@ +using Boilerplate.Client.Web; + +namespace Boilerplate.Tests.PageTests.BlazorWebAssembly; + +[TestClass] +public partial class IdentityPagesTests : BlazorServer.IdentityPagesTests +{ + public override BlazorWebAppMode BlazorRenderMode => BlazorWebAppMode.BlazorWebAssembly; +} + +[TestClass] +public partial class LocalizationTests : BlazorServer.LocalizationTests +{ + public override BlazorWebAppMode BlazorRenderMode => BlazorWebAppMode.BlazorWebAssembly; + +#if MultilingualEnabled == false + [TestMethod] + [TestCategory("MultilingualDisabled")] + public async Task MultilingualDisabled() + { + var homePage = new PageModels.MainHomePage(Page, WebAppServerAddress); + await homePage.Open(); + await homePage.AssertOpen(); + + var contains = Extensions.PlaywrightCacheExtensions.ContainsAsset(new(@"\/_framework\/icudt_hybrid\.dat\?v=sha256-.+")); + Assert.IsFalse(contains, "The 'icudt_hybrid.dat' file must not be loaded when Multilingual is disabled."); + } +#endif +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/IdentityPagesTests.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/IdentityPagesTests.cs new file mode 100644 index 0000000000..4e39e94f5b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/IdentityPagesTests.cs @@ -0,0 +1,324 @@ +//+:cnd:noEmit +using Boilerplate.Tests.Services; +using Boilerplate.Server.Api.Data; +using Boilerplate.Tests.PageTests.PageModels; +using Boilerplate.Tests.PageTests.PageModels.Identity; + +namespace Boilerplate.Tests.PageTests.BlazorServer; + +[TestClass] +public partial class IdentityPagesTests : PageTestBase +{ + [TestMethod] + public async Task UnauthorizedUser_Should_RedirectToSignInPage() + { + var response = await Page.GotoAsync(new Uri(WebAppServerAddress, Urls.SettingsPage).ToString()); + + Assert.IsNotNull(response); + Assert.AreEqual(StatusCodes.Status200OK, response.Status); + + await Expect(Page).ToHaveURLAsync(new Uri(WebAppServerAddress, "/sign-in?return-url=settings").ToString()); + } + + [TestMethod] + [DataRow("ValidCredentials")] + [DataRow("InvalidCredentials")] + public async Task SignIn(string mode) + { + var signInPage = new SignInPage(Page, WebAppServerAddress); + + await signInPage.Open(); + await signInPage.AssertOpen(); + + switch (mode) + { + case "ValidCredentials": + var identityHomePage = await signInPage.SignInWithEmail(); + await identityHomePage.AssertSignInSuccess(); + break; + case "InvalidCredentials": + await signInPage.SignInWithEmail(email: "invalid@bitplatform.dev", password: "invalid"); + await signInPage.AssertSignInFailed(); + break; + default: + throw new NotSupportedException(); + } + } + + [TestMethod] + public async Task SignOut() + { + await using var scope = TestServer.WebApp.Services.CreateAsyncScope(); + + var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); + var userService = new UserService(dbContext); + + var email = $"{Guid.NewGuid()}@gmail.com"; + var user = await userService.AddUser(email); + + var signInPage = new SignInPage(Page, WebAppServerAddress); + + await signInPage.Open(); + await signInPage.AssertOpen(); + + var identityHomePage = await signInPage.SignInWithEmail(email); + await identityHomePage.AssertSignInSuccess(email, userFullName: null); + + await dbContext.Entry(user).ReloadAsync(); + Assert.AreEqual(1, user.Sessions.Count); + + var mainHomePage = await identityHomePage.SignOut(); + await mainHomePage.AssertSignOut(); + + await dbContext.Entry(user).ReloadAsync(); + Assert.AreEqual(0, user.Sessions.Count); + } + + [TestMethod] + [DataRow("Token")] + [DataRow("InvalidToken")] + [DataRow("MagicLink")] + public async Task SignUp(string mode) + { + var signupPage = new SignUpPage(Page, WebAppServerAddress); + + await signupPage.Open(); + await signupPage.AssertOpen(); + + var email = $"{Guid.NewGuid()}@gmail.com"; + var confirmPage = await signupPage.SignUp(email); + await confirmPage.AssertOpen(); + + IdentityHomePage identityHomePage; + switch (mode) + { + case "Token": + var confirmationEmail = await signupPage.OpenConfirmationEmail(); + await confirmationEmail.AssertContent(); + var token = await confirmationEmail.GetToken(); + identityHomePage = await confirmPage.ConfirmByToken(token); + break; + case "MagicLink": + confirmationEmail = await signupPage.OpenConfirmationEmail(); + await confirmationEmail.AssertContent(); + identityHomePage = await confirmationEmail.OpenMagicLink<IdentityHomePage>(); + break; + case "InvalidToken": + await confirmPage.ConfirmByToken("111111"); + await confirmPage.AssertInvalidToken(); + return; + default: + throw new NotSupportedException(); + } + + await identityHomePage.AssertOpen(); + await identityHomePage.AssertSignInSuccess(email, userFullName: null); + } + + [TestMethod] + [DataRow("Token")] + [DataRow("InvalidToken")] + [DataRow("MagicLink")] + [DataRow("TooManyRequests")] + [DataRow("NotExisted")] + public async Task ForgotPassword(string mode) + { + var email = await CreateNewUser(); + + var forgotPasswordPage = new ForgotPasswordPage(Page, WebAppServerAddress); + + await forgotPasswordPage.Open(); + await forgotPasswordPage.AssertOpen(); + + if (mode is "NotExisted") + { + await forgotPasswordPage.ForgotPassword("not-existed@bitplatform.dev"); + await forgotPasswordPage.AssertUserNotFound(); + return; + } + + var resetPasswordPage = await forgotPasswordPage.ForgotPassword(email); + await resetPasswordPage.AssertOpen(); + + const string newPassword = "new_password"; + switch (mode) + { + case "Token": + var resetPasswordEmail = await forgotPasswordPage.OpenResetPasswordEmail(); + await resetPasswordEmail.AssertContent(); + var token = await resetPasswordEmail.GetToken(); + await resetPasswordPage.ContinueByToken(token); + break; + case "MagicLink": + resetPasswordEmail = await forgotPasswordPage.OpenResetPasswordEmail(); + await resetPasswordEmail.AssertContent(); + resetPasswordPage = await resetPasswordEmail.OpenMagicLink(); + break; + case "InvalidToken": + await resetPasswordPage.ContinueByToken("111111"); + await resetPasswordPage.SetPassword(newPassword); + await resetPasswordPage.AssertInvalidToken(); + return; + case "TooManyRequests": + await Page.GoBackAsync(); + await forgotPasswordPage.ForgotPassword(email); + await forgotPasswordPage.AssertTooManyRequests(); + return; + default: + throw new NotSupportedException(); + } + await resetPasswordPage.AssertValidToken(); + + await resetPasswordPage.SetPassword(newPassword); + await resetPasswordPage.AssertSetPassword(); + + var signInPage = new SignInPage(Page, WebAppServerAddress); + + await signInPage.Open(); + await signInPage.AssertOpen(); + + var identityHomePage = await signInPage.SignInWithEmail(email, newPassword); + await identityHomePage.AssertSignInSuccess(email, userFullName: null); + } + + [TestMethod] + [DataRow("Token")] + [DataRow("InvalidToken")] + [DataRow("MagicLink")] + [DataRow("TooManyRequests")] + public async Task ChangeEmail(string mode) + { + var email = await CreateNewUser(); + + var signInPage = new SignInPage(Page, WebAppServerAddress); + + await signInPage.Open(); + await signInPage.AssertOpen(); + + var identityHomePage = await signInPage.SignInWithEmail(email); + await identityHomePage.AssertSignInSuccess(email, userFullName: null); + + var settingsPage = new SettingsPage(Page, WebAppServerAddress); + + await settingsPage.Open(); + await settingsPage.AssertOpen(); + + await settingsPage.ExpandAccount(); + await settingsPage.AssertExpandAccount(email); + + var newEmail = $"{Guid.NewGuid()}@gmail.com"; + await settingsPage.ChangeEmail(newEmail); + await settingsPage.AssertChangeEmail(); + + switch (mode) + { + case "Token": + var confirmationEmail = await settingsPage.OpenConfirmationEmail(); + await confirmationEmail.AssertContent(); + var token = await confirmationEmail.GetToken(); + await settingsPage.ConfirmEmailByToken(token); + break; + case "MagicLink": + confirmationEmail = await settingsPage.OpenConfirmationEmail(); + await confirmationEmail.AssertContent(); + settingsPage = await confirmationEmail.OpenMagicLink(); + break; + case "InvalidToken": + await settingsPage.ConfirmEmailByToken("111111"); + await settingsPage.AssertEmailInvalidToken(); + return; + case "TooManyRequests": + await settingsPage.ClickOnPhoneTab(); + await settingsPage.ClickOnEmailTab(); + await settingsPage.ChangeEmail(newEmail); + await settingsPage.AssertTooManyRequestsForChangeEmail(); + return; + default: + throw new NotSupportedException(); + } + await settingsPage.AssertConfirmEmailSuccess(); + + signInPage = await settingsPage.SignOut(); + await signInPage.AssertOpen(); + await signInPage.AssertSignOut(); + + await signInPage.SignInWithEmail(email); + await signInPage.AssertSignInFailed(); + + settingsPage = await signInPage.SignInWithEmail<SettingsPage>(newEmail); + await settingsPage.AssertSignInSuccess(newEmail, userFullName: null); + } + + [TestMethod] + [DataRow("Token")] + [DataRow("InvalidToken")] + [DataRow("TooManyRequests")] + public async Task ChangePhone(string mode) + { + var email = await CreateNewUser(); + + var signInPage = new SignInPage(Page, WebAppServerAddress); + + await signInPage.Open(); + await signInPage.AssertOpen(); + + var identityHomePage = await signInPage.SignInWithEmail(email); + await identityHomePage.AssertSignInSuccess(email, userFullName: null); + + var settingsPage = new SettingsPage(Page, WebAppServerAddress); + + await settingsPage.Open(); + await settingsPage.AssertOpen(); + + await settingsPage.ExpandAccount(); + await settingsPage.ClickOnPhoneTab(); + await settingsPage.AssertPhoneTab(null); + + var phone = $"+1{Random.Shared.Next(1111111111, int.MaxValue)}"; + await settingsPage.ChangePhone(phone); + await settingsPage.AssertChangePhone(); + + switch (mode) + { + case "Token": + var token = settingsPage.GetPhoneToken(); + await settingsPage.ConfirmPhoneByToken(token); + await settingsPage.AssertConfirmPhoneSuccess(); + + signInPage = await settingsPage.SignOut(); + await signInPage.AssertOpen(); + await signInPage.AssertSignOut(); + + await signInPage.ClickOnPhoneTab(); + await signInPage.AssertPhoneTab(); + + settingsPage = await signInPage.SignInWithPhone<SettingsPage>(phone); + await settingsPage.AssertSignInSuccess(email, userFullName: null); + return; + case "InvalidToken": + await settingsPage.ConfirmPhoneByToken("111111"); + await settingsPage.AssertPhoneInvalidToken(); + return; + case "TooManyRequests": + await settingsPage.ClickOnEmailTab(); + await settingsPage.ClickOnPhoneTab(); + await settingsPage.ChangePhone(phone); + await settingsPage.AssertTooManyRequestsForChangePhone(); + return; + default: + throw new NotSupportedException(); + } + } + + private async Task<string> CreateNewUser() + { + await using var scope = TestServer.WebApp.Services.CreateAsyncScope(); + + var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); + var userService = new UserService(dbContext); + var email = $"{Guid.NewGuid()}@gmail.com"; + await userService.AddUser(email); + + return email; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/LocalizationTests.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/LocalizationTests.cs new file mode 100644 index 0000000000..59fe5c20bf --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/LocalizationTests.cs @@ -0,0 +1,68 @@ +using System.Reflection; +using Boilerplate.Tests.Services; +using Boilerplate.Tests.PageTests.PageModels; + +namespace Boilerplate.Tests.PageTests.BlazorServer; + +[TestClass] +public partial class LocalizationTests : PageTestBase +{ + [TestMethod] + [CultureData] + [ConfigureBrowserContext(nameof(SetCultureInBrowserContext))] + public async Task AcceptLanguageHeader(string cultureName, string cultureDisplayName) + { + var localizer = StringLocalizerFactory.Create<AppStrings>(cultureName); + var homePage = new MainHomePage(Page, WebAppServerAddress); + await homePage.Open(); + await homePage.AssertLocalized(localizer, cultureName, cultureDisplayName); + } + public async Task SetCultureInBrowserContext(BrowserNewContextOptions options, string cultureName, string _) => options.Locale = cultureName; + + [TestMethod] + [CultureData] + public async Task LanguageDropDown(string cultureName, string cultureDisplayName) + { + var localizer = StringLocalizerFactory.Create<AppStrings>(cultureName); + var homePage = new MainHomePage(Page, WebAppServerAddress); + await homePage.Open(); + await homePage.ChangeCulture(cultureDisplayName); + await homePage.AssertLocalized(localizer, cultureName, cultureDisplayName); + } + + [TestMethod] + [CultureData] + public async Task QueryString(string cultureName, string cultureDisplayName) + { + var localizer = StringLocalizerFactory.Create<AppStrings>(cultureName); + var homePage = new MainHomePage(Page, WebAppServerAddress); + await Page.GotoAsync($"{WebAppServerAddress}?culture={cultureName}"); + await homePage.AssertLocalized(localizer, cultureName, cultureDisplayName); + } + + [TestMethod] + [CultureData] + public async Task UrlSegment(string cultureName, string cultureDisplayName) + { + var localizer = StringLocalizerFactory.Create<AppStrings>(cultureName); + var homePage = new MainHomePage(Page, WebAppServerAddress); + await Page.GotoAsync(new Uri(WebAppServerAddress, cultureName).ToString()); + await homePage.AssertLocalized(localizer, cultureName, cultureDisplayName); + } +} + +public class CultureDataAttribute : Attribute, ITestDataSource +{ + public IEnumerable<object[]> GetData(MethodInfo methodInfo) + { + return CultureInfoManager.SupportedCultures.Select(p => new object[] { p.Culture.Name, p.DisplayName }); + //OR using below code + //yield return ["en-US", "English US"]; + //yield return ["fa-IR", "فارسی"]; + } + + public string GetDisplayName(MethodInfo methodInfo, object[] data) + { + return $"{methodInfo.Name} ({string.Join(", ", data)})"; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ConfirmationEmail.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ConfirmationEmail.cs new file mode 100644 index 0000000000..e99b175870 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ConfirmationEmail.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; +using Boilerplate.Server.Api.Resources; +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels.Email; + +public partial class ConfirmationEmail<TPage>(IBrowserContext context, Uri serverAddress) + : TokenMagicLinkEmail<TPage>(context, serverAddress) + where TPage : RootLayout +{ + protected override bool WaitForRedirectOnMagicLink => true; + protected override string EmailSubject => EmailStrings.ConfirmationEmailSubject.Replace("{0}", @"\b\d{6}\b"); + + protected override async Task AssertContentCore() + { + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.WelcomeToApp); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(new Regex(EmailStrings.EmailConfirmationMessageSubtitle.Replace("{0}", ".*"))); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.EmailConfirmationMessageBodyToken); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.EmailConfirmationMessageBodyLink); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ResetPasswordEmail.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ResetPasswordEmail.cs new file mode 100644 index 0000000000..1f5afbb006 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/ResetPasswordEmail.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; +using Boilerplate.Server.Api.Resources; +using Boilerplate.Tests.PageTests.PageModels.Identity; + +namespace Boilerplate.Tests.PageTests.PageModels.Email; + +public partial class ResetPasswordEmail(IBrowserContext context, Uri serverAddress) + : TokenMagicLinkEmail<ResetPasswordPage>(context, serverAddress) +{ + protected override bool WaitForRedirectOnMagicLink => false; + protected override string EmailSubject => EmailStrings.ResetPasswordEmailSubject.Replace("{0}", @"\b\d{6}\b"); + + protected override async Task AssertContentCore() + { + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(new Regex(EmailStrings.ResetPasswordTitle.Replace("{0}", ".*"))); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.ResetPasswordSubtitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.ResetPasswordBody); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.ResetPasswordTokenMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(EmailStrings.ResetPasswordLinkMessage); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/TokenMagicLinkEmail.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/TokenMagicLinkEmail.cs new file mode 100644 index 0000000000..57c2b2d857 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Email/TokenMagicLinkEmail.cs @@ -0,0 +1,66 @@ +using System.Text.RegularExpressions; +using Boilerplate.Tests.PageTests.PageModels.Layout; +using Boilerplate.Tests.Services; + +namespace Boilerplate.Tests.PageTests.PageModels.Email; + +public abstract partial class TokenMagicLinkEmail<TPage>(IBrowserContext context, Uri serverAddress) + where TPage : RootLayout +{ + private const string OpenEmailFirstMessage = $"You must call {nameof(Open)} method first"; + public Uri WebAppServerAddress => serverAddress; + public IPage Page { get; private set; } = null!; + public string DestinationPagePath => ((TPage)Activator.CreateInstance(typeof(TPage), Page, WebAppServerAddress)!).PagePath; + protected abstract bool WaitForRedirectOnMagicLink { get; } + protected abstract string EmailSubject { get; } + + public virtual async Task Open(string emailAddress) + { + var html = EmailReaderService.GetLastEmailFor(emailAddress, EmailSubject); + Page = await context.NewPageAsync(); + await Page.SetContentAsync(html); + } + + public virtual async Task AssertContent() + { + Assert.IsNotNull(Page, OpenEmailFirstMessage); + + await AssertContentCore(); + await Assertions.Expect(Page.GetByRole(AriaRole.Link, new() { Name = new Uri(WebAppServerAddress, DestinationPagePath).ToString() })).ToBeVisibleAsync(); + } + + protected abstract Task AssertContentCore(); + + public virtual async Task<string> GetToken() + { + Assert.IsNotNull(Page, OpenEmailFirstMessage); + + var token = await Page.GetByText(new Regex(@"^\d{6}$")).TextContentAsync(); + Assert.IsNotNull(token, "Confirmation token not found in email"); + return token; + } + + public async Task<TPage> OpenMagicLink() + { + return await OpenMagicLink<TPage>(); + } + + public virtual async Task<TFinalPage> OpenMagicLink<TFinalPage>() + where TFinalPage : RootLayout + { + Assert.IsNotNull(Page, OpenEmailFirstMessage); + + var magicLink = Page.GetByRole(AriaRole.Link, new() { Name = new Uri(WebAppServerAddress, DestinationPagePath).ToString() }); + var href = await magicLink.GetAttributeAsync("href"); + await magicLink.ClickAsync(); + + if (WaitForRedirectOnMagicLink) + { + //When opening the magic link, destination page usually redirects to itself or another page (e.g. SettingsPage) + //So we must wait for the redirection to complete + await Page.WaitForURLAsync(url => url != href); + } + + return (TFinalPage)Activator.CreateInstance(typeof(TFinalPage), Page, WebAppServerAddress)!; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/HomePage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/HomePage.cs new file mode 100644 index 0000000000..e1edf64c08 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/HomePage.cs @@ -0,0 +1,52 @@ +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels; + +public partial class MainHomePage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + public override string PagePath => Urls.HomePage; + public override string PageTitle => AppStrings.HomePageTitle; + + public async Task ChangeCulture(string cultureDisplayName) + { + await Page.GetByRole(AriaRole.Combobox).Locator($"//img[contains(@src, 'flags')]").ClickAsync(); + await Page.Locator($"button[role='option']:has-text('{cultureDisplayName}')").ClickAsync(); + } + + public async Task AssertLocalized(IStringLocalizer localizer, string cultureName, string cultureDisplayName) + { + await Assertions.Expect(Page).ToHaveTitleAsync(localizer[nameof(AppStrings.HomePageTitle)]); + + await Assertions.Expect(Page.GetByText(localizer[nameof(AppStrings.HomePanelTitle)] + " " + localizer[nameof(AppStrings.HomePanelSubtitle)])).ToBeVisibleAsync(); + + await Assertions.Expect(Page.GetByText(localizer[nameof(AppStrings.HomeMessage)])).ToBeVisibleAsync(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Link, new() { Name = localizer[nameof(AppStrings.SignIn)] })).ToBeVisibleAsync(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Link, new() { Name = localizer[nameof(AppStrings.SignUp)] })).ToBeVisibleAsync(); + + var cultureDropdown = Page.GetByRole(AriaRole.Combobox).Locator($"//img[contains(@src, 'flags/{cultureName}')]"); + + await Assertions.Expect(cultureDropdown).ToBeVisibleAsync(); + + await cultureDropdown.ClickAsync(); + + await Assertions.Expect(Page.Locator($"button[role='option']:has-text('{cultureDisplayName}')")).ToHaveAttributeAsync("aria-selected", "true"); + } +} + +public partial class IdentityHomePage(IPage page, Uri serverAddress) + : IdentityLayout(page, serverAddress) +{ + public override string PagePath => Urls.HomePage; + public override string PageTitle => AppStrings.HomePageTitle; + + public async Task<MainHomePage> SignOut() + { + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut }).ClickAsync(); + await Page.GetByRole(AriaRole.Dialog).GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut }).ClickAsync(); + + return new MainHomePage(Page, WebAppServerAddress); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ConfirmPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ConfirmPage.cs new file mode 100644 index 0000000000..de64bf5c0b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ConfirmPage.cs @@ -0,0 +1,57 @@ +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class ConfirmPage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + public override string PagePath => Urls.ConfirmPage; + public override string PageTitle => AppStrings.ConfirmPageTitle; + public string? EmailAddress { private get; init; } + + public override async Task AssertOpen() + { + await base.AssertOpen(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmTitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmEmailSubtitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmEmailMessage); + var emailInput = Page.GetByPlaceholder(AppStrings.EmailPlaceholder); + if (EmailAddress is null) + { + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToBeEnabledAsync(); + await Assertions.Expect(emailInput).ToBeEditableAsync(); + } + else + { + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToBeDisabledAsync(); + await Assertions.Expect(emailInput).Not.ToBeEditableAsync(); + } + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.EmailTokenPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.EmailTokenConfirmButtonText })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.NotReceivedEmailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.CheckSpamMailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ResendEmailTokenButtonText })).ToBeVisibleAsync(); + } + + public async Task<IdentityHomePage> ConfirmByToken(string token, string? email = null) + { + Assert.IsTrue(EmailAddress is not null || email is not null, "Either email address from query string or email input is required"); + Assert.IsTrue(EmailAddress is null || email is null, "Both email address from query string and email input cannot be used at the same time"); + + if (email is not null) + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(email); + + await Page.GetByPlaceholder(AppStrings.EmailTokenPlaceholder).FillAsync(token); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.EmailTokenConfirmButtonText }).ClickAsync(); + + return new(Page, WebAppServerAddress); + } + + public async Task AssertInvalidToken() + { + await Assertions.Expect(Page.GetByText(AppStrings.InvalidToken)).ToBeVisibleAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ForgotPasswordPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ForgotPasswordPage.cs new file mode 100644 index 0000000000..5b6b654c30 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ForgotPasswordPage.cs @@ -0,0 +1,56 @@ +using System.Text.RegularExpressions; +using Boilerplate.Tests.PageTests.PageModels.Email; +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class ForgotPasswordPage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + private string? email; + public override string PagePath => Urls.ForgotPasswordPage; + public override string PageTitle => AppStrings.ForgotPasswordTitle; + + public override async Task AssertOpen() + { + await base.AssertOpen(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ForgotPasswordTitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ForgotPasswordMessage); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.EmailPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPasswordMessageInForgot); + var resetPasswordLink = Page.GetByRole(AriaRole.Link, new() { Name = AppStrings.ResetPassword }); + await Assertions.Expect(resetPasswordLink).ToBeVisibleAsync(); + await Assertions.Expect(resetPasswordLink).ToHaveAttributeAsync("href", Urls.ResetPasswordPage); + } + + public async Task<ResetPasswordPage> ForgotPassword(string email = TestData.DefaultTestEmail) + { + this.email = email; + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(email); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit }).ClickAsync(); + + return new(Page, WebAppServerAddress) { EmailAddress = email }; + } + + public async Task AssertUserNotFound() + { + await Assertions.Expect(Page.GetByText(AppStrings.UserNotFound)).ToBeVisibleAsync(); + } + + public async Task AssertTooManyRequests() + { + var pattern = new Regex(AppStrings.WaitForResetPasswordTokenRequestResendDelay.Replace("{0}", ".*")); + await Assertions.Expect(Page.GetByText(pattern)).ToBeVisibleAsync(); + } + + public async Task<ResetPasswordEmail> OpenResetPasswordEmail() + { + Assert.IsNotNull(email, $"Call {nameof(ForgotPassword)} method first."); + + var resetPasswordEmail = new ResetPasswordEmail(Page.Context, WebAppServerAddress); + await resetPasswordEmail.Open(email); + return resetPasswordEmail; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ResetPasswordPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ResetPasswordPage.cs new file mode 100644 index 0000000000..79c86fe8c3 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/ResetPasswordPage.cs @@ -0,0 +1,75 @@ +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class ResetPasswordPage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + public override string PagePath => Urls.ResetPasswordPage; + public override string PageTitle => AppStrings.ResetPasswordTitle; + public string? EmailAddress { private get; init; } + + public override async Task AssertOpen() + { + await base.AssertOpen(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPassword); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPasswordSubtitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPasswordMessage); + var emailInput = Page.GetByPlaceholder(AppStrings.EmailPlaceholder); + if (EmailAddress is null) + { + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToBeEnabledAsync(); + await Assertions.Expect(emailInput).ToBeEditableAsync(); + } + else + { + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToBeDisabledAsync(); + await Assertions.Expect(emailInput).Not.ToBeEditableAsync(); + } + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.TokenPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Continue })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.NotReceivedEmailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.CheckSpamMailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Resend })).ToBeVisibleAsync(); + } + + public async Task ContinueByToken(string token, string? email = null) + { + Assert.IsTrue(EmailAddress is not null || email is not null, "Either email address from query string or email input is required"); + Assert.IsTrue(EmailAddress is null || email is null, "Both email address from query string and email input cannot be used at the same time"); + + if (email is not null) + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(email); + + await Page.GetByPlaceholder(AppStrings.TokenPlaceholder).FillAsync(token); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Continue }).ClickAsync(); + } + + public async Task AssertInvalidToken() + { + await Assertions.Expect(Page.GetByText(AppStrings.InvalidToken)).ToBeVisibleAsync(); + } + + public async Task AssertValidToken() + { + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.PasswordPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.ConfirmPassword)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ResetPasswordButtonText })).ToBeVisibleAsync(); + } + + public async Task SetPassword(string newPassword) + { + await Page.GetByPlaceholder(AppStrings.PasswordPlaceholder).FillAsync(newPassword); + await Page.GetByPlaceholder(AppStrings.ConfirmPassword).FillAsync(newPassword); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ResetPasswordButtonText }).ClickAsync(); + } + + public async Task AssertSetPassword() + { + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPasswordSuccessTitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ResetPasswordSuccessBody); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Email.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Email.cs new file mode 100644 index 0000000000..c00cf39407 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Email.cs @@ -0,0 +1,80 @@ +using System.Text.RegularExpressions; +using Boilerplate.Tests.PageTests.PageModels.Email; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class SettingsPage +{ + public async Task ClickOnEmailTab() + { + await Page.GetByRole(AriaRole.Tab, new() { Name = AppStrings.Email }).ClickAsync(); + } + + public async Task AssertEmailTab(string userEmail = TestData.DefaultTestEmail) + { + var emailInput = Page.GetByLabel(AppStrings.Email, new() { Exact = true }).Locator("span"); + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToContainTextAsync(userEmail); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.NewEmailPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmMessageInProfile); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Confirm })).ToBeVisibleAsync(); + } + + public async Task ChangeEmail(string newEmail) + { + this.newEmail = newEmail; + await Page.GetByPlaceholder(AppStrings.NewEmailPlaceholder).FillAsync(newEmail); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit }).ClickAsync(); + } + + public async Task AssertChangeEmail() + { + await Assertions.Expect(Page.GetByText(AppStrings.SuccessfulSendChangeEmailTokenMessage)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmEmailSubtitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmEmailMessage); + var emailInput = Page.GetByPlaceholder(AppStrings.EmailPlaceholder); + await Assertions.Expect(emailInput).ToBeVisibleAsync(); + await Assertions.Expect(emailInput).ToBeDisabledAsync(); + await Assertions.Expect(emailInput).Not.ToBeEditableAsync(); + await Assertions.Expect(emailInput).ToHaveValueAsync(newEmail); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.EmailTokenPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.EmailTokenConfirmButtonText })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.NotReceivedEmailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.CheckSpamMailMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ResendEmailTokenButtonText })).ToBeVisibleAsync(); + } + + public async Task AssertTooManyRequestsForChangeEmail() + { + var pattern = new Regex(AppStrings.WaitForEmailTokenRequestResendDelay.Replace("{0}", ".*")); + await Assertions.Expect(Page.GetByText(pattern)).ToBeVisibleAsync(); + } + + public async Task<ConfirmationEmail<SettingsPage>> OpenConfirmationEmail() + { + Assert.IsNotNull(newEmail, $"Call {nameof(ChangeEmail)} method first."); + + var confirmationEmail = new ConfirmationEmail<SettingsPage>(Page.Context, WebAppServerAddress); + await confirmationEmail.Open(newEmail); + return confirmationEmail; + } + + public async Task ConfirmEmailByToken(string token) + { + await Page.GetByPlaceholder(AppStrings.EmailTokenPlaceholder).FillAsync(token); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.EmailTokenConfirmButtonText }).ClickAsync(); + } + + public async Task AssertConfirmEmailSuccess() + { + await Assertions.Expect(Page.Locator(".bit-prs.persona").Last).ToContainTextAsync(newEmail); + await ExpandAccount(); + await AssertExpandAccount(newEmail); + } + + public async Task AssertEmailInvalidToken() + { + await Assertions.Expect(Page.GetByText(AppStrings.InvalidToken)).ToBeVisibleAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Phone.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Phone.cs new file mode 100644 index 0000000000..8e32dec93f --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.Account.Phone.cs @@ -0,0 +1,84 @@ +using System.Text.RegularExpressions; +using Boilerplate.Tests.Services; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class SettingsPage +{ + public async Task ClickOnPhoneTab() + { + await Page.GetByRole(AriaRole.Tab, new() { Name = AppStrings.Phone }).ClickAsync(); + } + + public async Task AssertPhoneTab(string? phone) + { + var phoneInput = Page.GetByLabel(AppStrings.Phone, new() { Exact = true }).Locator("span"); + if (phone is null) + { + await Assertions.Expect(phoneInput).ToBeHiddenAsync(); + } + else + { + await Assertions.Expect(phoneInput).ToBeVisibleAsync(); + await Assertions.Expect(phoneInput).ToContainTextAsync(phone); + } + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.NewPhoneNumberPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmMessageInProfile); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Confirm })).ToBeVisibleAsync(); + } + + public async Task ChangePhone(string newPhone) + { + this.newPhone = newPhone; + await Page.GetByPlaceholder(AppStrings.NewPhoneNumberPlaceholder).FillAsync(newPhone); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.Submit }).ClickAsync(); + } + + public async Task AssertChangePhone() + { + await Assertions.Expect(Page.GetByText(AppStrings.SuccessfulSendChangePhoneNumberTokenMessage)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmPhoneSubtitle); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.ConfirmPhoneMessage); + var phoneInput = Page.GetByPlaceholder(AppStrings.PhoneNumberPlaceholder); + await Assertions.Expect(phoneInput).ToBeVisibleAsync(); + await Assertions.Expect(phoneInput).ToBeDisabledAsync(); + await Assertions.Expect(phoneInput).Not.ToBeEditableAsync(); + await Assertions.Expect(phoneInput).ToHaveValueAsync(newPhone); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.PhoneTokenPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.PhoneTokenConfirmButtonText })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.NotReceivedPhoneMessage); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ResendPhoneTokenButtonText })).ToBeVisibleAsync(); + } + + public async Task AssertTooManyRequestsForChangePhone() + { + var pattern = new Regex(AppStrings.WaitForPhoneNumberTokenRequestResendDelay.Replace("{0}", ".*")); + await Assertions.Expect(Page.GetByText(pattern)).ToBeVisibleAsync(); + } + + public string GetPhoneToken() + { + var pattern = AppStrings.ChangePhoneNumberTokenSmsText.Replace("{0}", @"\b\d{6}\b"); + return FakePhoneService.GetLastOtpFor(newPhone, pattern); + } + + public async Task ConfirmPhoneByToken(string token) + { + await Page.GetByPlaceholder(AppStrings.PhoneTokenPlaceholder).FillAsync(token); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.PhoneTokenConfirmButtonText }).ClickAsync(); + } + + public async Task AssertConfirmPhoneSuccess() + { + await Page.WaitForTimeoutAsync(1000); //Wait for redirection to complete + await ExpandAccount(); + await ClickOnPhoneTab(); + await AssertPhoneTab(newPhone); + } + + public async Task AssertPhoneInvalidToken() + { + await Assertions.Expect(Page.GetByText(AppStrings.ResourceValidationException)).ToBeVisibleAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.cs new file mode 100644 index 0000000000..0ea12e049c --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SettingsPage.cs @@ -0,0 +1,36 @@ +using Boilerplate.Tests.PageTests.PageModels.Layout; +using Microsoft.AspNetCore.WebUtilities; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class SettingsPage(IPage page, Uri serverAddress) + : IdentityLayout(page, serverAddress) +{ + private string newEmail = QueryHelpers.ParseQuery(new Uri(page.Url).Query).GetValueOrDefault("email").ToString(); + private string newPhone; + public override string PagePath => Urls.SettingsPage; + public override string PageTitle => AppStrings.Settings; + + public override async Task AssertOpen() + { + await base.AssertOpen(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.ProfileSubtitle })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.AccountSubtitle })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.TfaSubtitle })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SessionsSubtitle })).ToBeVisibleAsync(); + } + + public async Task ExpandAccount() + { + var accountButton = Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.AccountSubtitle }); + var isExpanded = (await accountButton.GetAttributeAsync("aria-expanded")) is not null; + if (isExpanded is false) + await accountButton.ClickAsync(); + } + + public async Task AssertExpandAccount(string userEmail = TestData.DefaultTestEmail) + { + await AssertEmailTab(userEmail); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignInPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignInPage.cs new file mode 100644 index 0000000000..34c7049b06 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignInPage.cs @@ -0,0 +1,141 @@ +using Boilerplate.Tests.PageTests.PageModels.Layout; +using Microsoft.AspNetCore.WebUtilities; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class SignInPage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + public override string PagePath => Urls.SignInPage; + public override string PageTitle => AppStrings.SignInPageTitle; + public string? ReturnUrl { private get; init; } + + public override async Task AssertOpen() + { + await base.AssertOpen(); + await AssertTab(Tab.Email); + } + + public async Task ClickOnEmailTab() + { + await Page.GetByRole(AriaRole.Tab, new() { Name = AppStrings.Email }).ClickAsync(); + } + + public async Task ClickOnPhoneTab() + { + await Page.GetByRole(AriaRole.Tab, new() { Name = AppStrings.PhoneNumber }).ClickAsync(); + } + + public async Task AssertEmailTab() + { + await AssertTab(Tab.Email); + } + + public async Task AssertPhoneTab() + { + await AssertTab(Tab.Phone); + } + + private async Task AssertTab(Tab tab) + { + AssertReturnUrl(); + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.SignInPanelSubtitle); + switch (tab) + { + case Tab.Email: + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.EmailPlaceholder)).ToBeVisibleAsync(); + break; + case Tab.Phone: + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.PhoneNumberPlaceholder)).ToBeVisibleAsync(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(tab), tab, null); + } + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.PasswordPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn })).ToBeVisibleAsync(); + var forgotPassswordLink = Page.GetByRole(AriaRole.Link, new() { Name = AppStrings.ForgotPasswordLink }); + await Assertions.Expect(forgotPassswordLink).ToBeVisibleAsync(); + await Assertions.Expect(forgotPassswordLink).ToHaveAttributeAsync("href", Urls.ForgotPasswordPage); + await Assertions.Expect(Page.GetByLabel(AppStrings.RememberMe)).ToBeCheckedAsync(); + } + + public async Task<IdentityHomePage> SignInWithEmail(string email = TestData.DefaultTestEmail, string password = TestData.DefaultTestPassword) + { + return await SignInWithEmail<IdentityHomePage>(email, password); + } + + public async Task<TPage> SignInWithEmail<TPage>(string email = TestData.DefaultTestEmail, string password = TestData.DefaultTestPassword) + where TPage : IdentityLayout + { + return await SignInCore<TPage>(Tab.Email, email, password); + } + + public async Task<IdentityHomePage> SignInWithPhone(string phone, string password = TestData.DefaultTestPassword) + { + return await SignInWithPhone<IdentityHomePage>(phone, password); + } + + public async Task<TPage> SignInWithPhone<TPage>(string phone, string password = TestData.DefaultTestPassword) + where TPage : IdentityLayout + { + return await SignInCore<TPage>(Tab.Phone, phone, password); + } + + private async Task<TPage> SignInCore<TPage>(Tab tab, string emailOrPhone, string password) + where TPage : IdentityLayout + { + switch (tab) + { + case Tab.Email: + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(emailOrPhone); + break; + case Tab.Phone: + await Page.GetByPlaceholder(AppStrings.PhoneNumberPlaceholder).FillAsync(emailOrPhone); + break; + default: + throw new ArgumentOutOfRangeException(nameof(tab), tab, null); + } + await Page.GetByPlaceholder(AppStrings.PasswordPlaceholder).FillAsync(password); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn }).ClickAsync(); + + AssertReturnUrl(); + if (ReturnUrl is null) + Assert.AreEqual(typeof(TPage), typeof(IdentityHomePage)); + + return (TPage)Activator.CreateInstance(typeof(TPage), Page, WebAppServerAddress)!; + } + + public async Task AssertSignInFailed() + { + await Assertions.Expect(Page.GetByText(AppStrings.InvalidUserCredentials)).ToBeVisibleAsync(); + await Assertions.Expect(Page.Locator(".bit-prs.persona")).ToBeHiddenAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut })).ToBeHiddenAsync(); + } + + public override async Task AssertSignOut() + { + await Assertions.Expect(Page.Locator(".bit-prs.persona")).ToBeHiddenAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut })).ToBeHiddenAsync(); + + if (ReturnUrl is null) + await Assertions.Expect(Page).ToHaveURLAsync(new Uri(WebAppServerAddress, Urls.SignInPage).ToString()); + else + await Assertions.Expect(Page).ToHaveURLAsync(new Uri(WebAppServerAddress, $"{Urls.SignInPage}?return-url={ReturnUrl}").ToString()); + } + + private void AssertReturnUrl() + { + var returnUrl = QueryHelpers.ParseQuery(new Uri(Page.Url).Query).GetValueOrDefault("return-url").ToString(); + if (ReturnUrl is null) + Assert.AreEqual(string.Empty, returnUrl); + else + Assert.AreEqual(ReturnUrl, returnUrl); + } + + private enum Tab + { + Email, + Phone + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignUpPage.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignUpPage.cs new file mode 100644 index 0000000000..8d6a5e79cd --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Identity/SignUpPage.cs @@ -0,0 +1,53 @@ +//+:cnd:noEmit +using Boilerplate.Tests.PageTests.PageModels.Email; +using Boilerplate.Tests.PageTests.PageModels.Layout; + +namespace Boilerplate.Tests.PageTests.PageModels.Identity; + +public partial class SignUpPage(IPage page, Uri serverAddress) + : MainLayout(page, serverAddress) +{ + private string? email; + public override string PagePath => Urls.SignUpPage; + public override string PageTitle => AppStrings.SignUpPageTitle; + + public override async Task Open() + { + await base.Open(); + + //#if (captcha == "reCaptcha") + //Override behavior of the javascript recaptcha function on the browser + await Page.WaitForFunctionAsync("window.grecaptcha?.getResponse !== undefined"); + await Page.EvaluateAsync("window.grecaptcha.getResponse = () => 'not-empty';"); + //#endif + } + + public override async Task AssertOpen() + { + await base.AssertOpen(); + + await Assertions.Expect(Page.GetByRole(AriaRole.Main)).ToContainTextAsync(AppStrings.SignUp); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.EmailPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByPlaceholder(AppStrings.PasswordPlaceholder)).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignUp, Exact = true })).ToBeVisibleAsync(); + } + + public async Task<ConfirmPage> SignUp(string email, string password = TestData.DefaultTestPassword) + { + this.email = email; + await Page.GetByPlaceholder(AppStrings.EmailPlaceholder).FillAsync(email); + await Page.GetByPlaceholder(AppStrings.PasswordPlaceholder).FillAsync(password); + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignUp, Exact = true }).ClickAsync(); + + return new(Page, WebAppServerAddress) { EmailAddress = email }; + } + + public async Task<ConfirmationEmail<ConfirmPage>> OpenConfirmationEmail() + { + Assert.IsNotNull(email, $"Call {nameof(SignUp)} method first."); + + var confirmationEmail = new ConfirmationEmail<ConfirmPage>(Page.Context, WebAppServerAddress); + await confirmationEmail.Open(email); + return confirmationEmail; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/IdentityLayout.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/IdentityLayout.cs new file mode 100644 index 0000000000..90df31e737 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/IdentityLayout.cs @@ -0,0 +1,27 @@ +using Boilerplate.Tests.PageTests.PageModels.Identity; + +namespace Boilerplate.Tests.PageTests.PageModels.Layout; + +public abstract partial class IdentityLayout(IPage page, Uri serverAddress) + : RootLayout(page, serverAddress) +{ + public async Task AssertSignInSuccess(string userEmail = TestData.DefaultTestEmail, string? userFullName = TestData.DefaultTestFullName) + { + var displayName = string.IsNullOrWhiteSpace(userFullName) ? userEmail : userFullName; + + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = displayName })).ToBeVisibleAsync(); + await Assertions.Expect(Page.Locator(".bit-prs.persona").First).ToContainTextAsync(displayName); + await Assertions.Expect(Page.Locator(".bit-prs.persona").Last).ToContainTextAsync(displayName); + await Assertions.Expect(Page.Locator(".bit-prs.persona").Last).ToContainTextAsync(userEmail); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut })).ToBeVisibleAsync(); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignIn })).ToBeHiddenAsync(); + } + + public async Task<SignInPage> SignOut() + { + await Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut }).ClickAsync(); + await Page.GetByRole(AriaRole.Dialog).GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut }).ClickAsync(); + + return new SignInPage(Page, WebAppServerAddress) { ReturnUrl = new Uri(Page.Url).PathAndQuery.TrimStart('/') }; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/MainLayout.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/MainLayout.cs new file mode 100644 index 0000000000..0682f66957 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/MainLayout.cs @@ -0,0 +1,12 @@ +namespace Boilerplate.Tests.PageTests.PageModels.Layout; + +public abstract partial class MainLayout(IPage page, Uri serverAddress) + : RootLayout(page, serverAddress) +{ + public virtual async Task AssertSignOut() + { + await Assertions.Expect(Page).ToHaveURLAsync(new Uri(WebAppServerAddress, PagePath).ToString()); + await Assertions.Expect(Page.GetByRole(AriaRole.Button, new() { Name = AppStrings.SignOut })).ToBeHiddenAsync(); + await Assertions.Expect(Page.Locator(".bit-prs.persona")).ToBeHiddenAsync(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/RootLayout.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/RootLayout.cs new file mode 100644 index 0000000000..69ae2b9d13 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageModels/Layout/RootLayout.cs @@ -0,0 +1,19 @@ +namespace Boilerplate.Tests.PageTests.PageModels.Layout; + +public abstract partial class RootLayout(IPage page, Uri serverAddress) +{ + public abstract string PagePath { get; } + public abstract string PageTitle { get; } + public IPage Page => page; + public Uri WebAppServerAddress => serverAddress; + + public virtual async Task Open() + { + await Page.GotoAsync(new Uri(WebAppServerAddress, PagePath).ToString()); + } + + public virtual async Task AssertOpen() + { + await Assertions.Expect(Page).ToHaveTitleAsync(PageTitle); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageTestBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageTestBase.cs new file mode 100644 index 0000000000..a00681d4bd --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/PageTests/PageTestBase.cs @@ -0,0 +1,113 @@ +using System.Reflection; +using Boilerplate.Client.Web; +using Boilerplate.Tests.Extensions; +using Microsoft.AspNetCore.Builder; + +namespace Boilerplate.Tests.PageTests; + +[TestClass] +public abstract partial class PageTestBase : PageTest +{ + public AppTestServer TestServer { get; set; } = new(); + public WebApplication WebApp => TestServer.WebApp; + public Uri WebAppServerAddress => TestServer.WebAppServerAddress; + public virtual BlazorWebAppMode BlazorRenderMode => BlazorWebAppMode.BlazorServer; + + [TestInitialize] + public async Task InitializeTestServer() + { + await Context.EnableBlazorWasmCaching(); + + var currentTestMethod = GetType().GetMethod(TestContext.TestName!); + + var configureTestServer = currentTestMethod!.GetCustomAttribute<ConfigureTestServerAttribute>(); + if (configureTestServer is not null) + { + var configureTestServerMethod = GetType() + .GetMethod(configureTestServer.MethodName, BindingFlags.Public | BindingFlags.Instance) + ?? throw new InvalidOperationException($"Method '{configureTestServer.MethodName}' not found."); + var currentTestMethodArguments = GetTestMethodArguments(); + await (Task)configureTestServerMethod.Invoke(this, [TestServer, .. currentTestMethodArguments])!; + return; + } + + var autoStartTestServer = currentTestMethod!.GetCustomAttribute<AutoStartTestServerAttribute>(); + if (autoStartTestServer is null || autoStartTestServer.AutoStart) + { + await TestServer.Build(configureTestConfigurations: configuration => + { + configuration["WebAppRender:BlazorMode"] = BlazorRenderMode.ToString(); + }).Start(); + } + } + + [TestCleanup] + public async ValueTask CleanupTestServer() + { + await this.FinalizeVideoRecording(); + + if (TestServer is not null) + { + await TestServer.DisposeAsync(); + } + } + + public override BrowserNewContextOptions ContextOptions() + { + var options = base.ContextOptions(); + options.EnableVideoRecording(TestContext); + + var currentTestMethod = GetType().GetMethod(TestContext.TestName!); + + var isAuthenticated = currentTestMethod!.GetCustomAttribute<AutoAuthenticateAttribute>() is not null; + if (isAuthenticated) + { + options.StorageState = TestsInitializer.AuthenticationState.Replace("[ServerAddress]", WebAppServerAddress.OriginalString); + } + + var configureBrowserContext = currentTestMethod!.GetCustomAttribute<ConfigureBrowserContextAttribute>(); + if (configureBrowserContext is not null) + { + var configureBrowserContextMethod = GetType() + .GetMethod(configureBrowserContext.MethodName, BindingFlags.Public | BindingFlags.Instance) + ?? throw new InvalidOperationException($"Method '{configureBrowserContext.MethodName}' not found."); + var currentTestMethodArguments = GetTestMethodArguments(); + configureBrowserContextMethod.Invoke(this, [options, .. currentTestMethodArguments]); + } + + return options; + } + + private string?[]? GetTestMethodArguments() + { + var testContextType = TestContext.GetType(); + var testMethodField = testContextType.GetField("_testMethod", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("Field '_testMethod' not found."); + var testMethod = testMethodField.GetValue(TestContext) ?? throw new InvalidOperationException("Field '_testMethod' is null."); + var testMethodType = testMethod.GetType(); + var serializedDataProperty = testMethodType.GetProperty("SerializedData", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("Property 'SerializedData' not found."); + var serializedData = (string?[]?)serializedDataProperty.GetValue(testMethod); + if (serializedData is null) return []; + return serializedData.Where((_, index) => index % 2 == 1).Select(x => x?.Trim('"')).ToArray(); + } +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class AutoAuthenticateAttribute : Attribute; + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class ConfigureBrowserContextAttribute(string methodName) : Attribute +{ + public string MethodName => methodName; +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class ConfigureTestServerAttribute(string methodName) : Attribute +{ + public string MethodName => methodName; +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class AutoStartTestServerAttribute(bool autoStart = true) : Attribute +{ + public bool AutoStart => autoStart; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/SampleApiTest.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/SampleApiTest.cs deleted file mode 100644 index b57b5d245e..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/SampleApiTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Boilerplate.Shared.Dtos.Identity; - -namespace Boilerplate.Tests; - -[TestClass] -public partial class SampleApiTest -{ - [TestMethod] - public async Task SignInTest() - { - await using var server = new AppTestServer(); - - await server.Build(services => - { - // Services registered in this test project will be used instead of the application's services, allowing you to fake certain behaviors during testing. - }).Start(); - - var client = server.CreateClient(); - - using var response = await client.PostAsJsonAsync("/api/Identity/SignIn", new SignInRequestDto - { - Email = "test@bitplatform.dev", - Password = "123456" - }); - - var signInResponse = await response.EnsureSuccessStatusCode().Content.ReadFromJsonAsync<SignInResponseDto>(); - - client.DefaultRequestHeaders.Authorization = new("Bearer", signInResponse!.AccessToken); - - var user = await client.GetFromJsonAsync<UserDto>("api/User/GetCurrentUser"); - - Assert.AreEqual(Guid.Parse("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), user!.Id); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/CulturedStringLocalizer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/CulturedStringLocalizer.cs new file mode 100644 index 0000000000..161852a12a --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/CulturedStringLocalizer.cs @@ -0,0 +1,50 @@ +using System.Collections.Concurrent; + +namespace Boilerplate.Tests.Services; + +public static class StringLocalizerFactory +{ + private static readonly ConcurrentDictionary<(string, Type), IStringLocalizer> _stringLocalizerCache = new(); + + public static IStringLocalizer<TResourceSource> Create<TResourceSource>(string culture) + { + ArgumentException.ThrowIfNullOrEmpty(culture); + + var localizer = _stringLocalizerCache.GetOrAdd((culture, typeof(TResourceSource)), + (_) => new CulturedStringLocalizer<TResourceSource>(culture)); + return (IStringLocalizer<TResourceSource>)localizer; + } +} + +public class CulturedStringLocalizer<TResourceSource>(string culture) : IStringLocalizer<TResourceSource> +{ + private readonly CultureInfo _cultureInfo = CultureInfoManager.CreateCultureInfo(culture); + private readonly ResourceManager _resourceManager = new(typeof(TResourceSource)); + + public LocalizedString this[string name] + { + get + { + ArgumentNullException.ThrowIfNull(name); + + var value = _resourceManager.GetString(name, _cultureInfo); + + return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceManager.BaseName); + } + } + + public LocalizedString this[string name, params object[] arguments] + { + get + { + ArgumentNullException.ThrowIfNull(name); + + var format = _resourceManager.GetString(name, _cultureInfo); + var value = string.Format(_cultureInfo, format ?? name, arguments); + + return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceManager.BaseName); + } + } + + public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) => throw new NotImplementedException(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/EmailReaderService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/EmailReaderService.cs new file mode 100644 index 0000000000..4ab1738182 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/EmailReaderService.cs @@ -0,0 +1,34 @@ +using Boilerplate.Server.Api.Resources; +using System.Text.RegularExpressions; +using MsgReader.Mime; + +namespace Boilerplate.Tests.Services; +public static partial class EmailReaderService +{ + public static string GetLastEmailFor(string toMailAddress, string subjectPattern) + { + ArgumentException.ThrowIfNullOrEmpty(toMailAddress); + ArgumentException.ThrowIfNullOrEmpty(subjectPattern); + + var emailsDirectory = Path.Combine(AppContext.BaseDirectory, "App_Data", "sent-emails"); + var messages = new DirectoryInfo(emailsDirectory).GetFiles().Select(Message.Load); + var message = messages + .Where(m => m.Headers.To[0].Address == toMailAddress) + .MaxBy(m => m.Headers.DateSent); + + Assert.IsNotNull(message, "Email has not sent"); + Assert.AreEqual("info@Boilerplate.com", message.Headers.From.Address); //TODO: read from AppSettings.Email.DefaultFromEmail + Assert.AreEqual(EmailStrings.DefaultFromName, message.Headers.From.DisplayName); + + if (subjectPattern is not null && Regex.IsMatch(message.Headers.Subject, subjectPattern) is false) + { + throw new AssertFailedException($@" + Email subject does not match. + expected pattern: {subjectPattern} + actual subject: {message.Headers.Subject} + "); + } + + return message.HtmlBody.GetBodyAsText(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakeGoogleRecaptchaHttpClient.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakeGoogleRecaptchaHttpClient.cs new file mode 100644 index 0000000000..51dd365e4d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakeGoogleRecaptchaHttpClient.cs @@ -0,0 +1,13 @@ +using Boilerplate.Server.Api.Services; + +namespace Boilerplate.Tests.Services; + +public partial class FakeGoogleRecaptchaHttpClient : GoogleRecaptchaHttpClient +{ + public FakeGoogleRecaptchaHttpClient() : base(null, null) { } + + public override ValueTask<bool> Verify(string? googleRecaptchaResponse, CancellationToken cancellationToken) + { + return ValueTask.FromResult(true); + } +} \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakePhoneService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakePhoneService.cs new file mode 100644 index 0000000000..d6be667b77 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/FakePhoneService.cs @@ -0,0 +1,72 @@ +using System.Collections.Concurrent; +using System.Text.RegularExpressions; +using Boilerplate.Server.Api; +using Boilerplate.Server.Api.Services; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using PhoneNumbers; + +namespace Boilerplate.Tests.Services; + +public partial class FakePhoneService(ServerApiSettings appSettings, IHostEnvironment hostEnvironment, IHttpContextAccessor httpContextAccessor, ILogger<PhoneService> logger, PhoneNumberUtil phoneNumberUtil) + : PhoneService(appSettings, hostEnvironment, httpContextAccessor, logger, phoneNumberUtil) +{ + private static readonly ConcurrentDictionary<string, string> LastSmsPerPhone = new(); + + public override Task SendSms(string messageText, string phoneNumber, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrEmpty(messageText); + ArgumentException.ThrowIfNullOrEmpty(phoneNumber); + + LastSmsPerPhone.AddOrUpdate(phoneNumber, messageText, (_, _) => messageText); + return Task.CompletedTask; + } + + public static string GetLastSmsFor(string phoneNumber, string pattern) + { + ArgumentException.ThrowIfNullOrEmpty(phoneNumber); + ArgumentException.ThrowIfNullOrEmpty(pattern); + + if (LastSmsPerPhone.TryGetValue(phoneNumber, out var message) is false) + Assert.IsNotNull(message, "Sms has not sent"); + + if (pattern is not null && Regex.IsMatch(message, pattern) is false) + { + throw new AssertFailedException($""" + Sms text does not match. + expected pattern: {pattern} + actual text: {message} + """); + } + + return message; + } + + /// <summary> + /// Extracts the last OTP sent to the specified phone number. + /// </summary> + /// <param name="phoneNumber">The phone number to check</param> + /// <returns>The 6-digit OTP from the last SMS</returns> + /// <exception cref="AssertException">Thrown when no valid OTP was found in the message</exception> + public static string GetLastOtpFor(string phoneNumber, string pattern) + { + ArgumentException.ThrowIfNullOrEmpty(phoneNumber); + ArgumentException.ThrowIfNullOrEmpty(pattern); + + var message = GetLastSmsFor(phoneNumber, pattern); + var otp = Regex.Match(message, @"\b\d{6}\b").Value; + Assert.IsNotNull(otp, $"No valid 6-digit OTP found in message: {message}"); + return otp; + } + + public static void Remove(string phoneNumber) + { + ArgumentException.ThrowIfNullOrEmpty(phoneNumber); + LastSmsPerPhone.TryRemove(phoneNumber, out _); + } + + public static void Clear() + { + LastSmsPerPhone.Clear(); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestAuthTokenProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestAuthTokenProvider.cs new file mode 100644 index 0000000000..1551ab61ba --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestAuthTokenProvider.cs @@ -0,0 +1,13 @@ +using Boilerplate.Client.Core.Services.Contracts; + +namespace Boilerplate.Tests.Services; + +public partial class TestAuthTokenProvider : IAuthTokenProvider +{ + [AutoInject] private IStorageService storageService = default!; + + public async Task<string?> GetAccessToken() + { + return await storageService.GetItem("access_token"); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestStorageService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestStorageService.cs new file mode 100644 index 0000000000..5ae84de65b --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/TestStorageService.cs @@ -0,0 +1,35 @@ +using Boilerplate.Client.Core.Services.Contracts; + +namespace Boilerplate.Tests.Services; + +/// <summary> +/// In UI tests, browser will uses its own storage, but for api tests, we need to fake the storage. +/// </summary> +public partial class TestStorageService : IStorageService +{ + private readonly Dictionary<string, string?> tempStorage = []; + + public async ValueTask<string?> GetItem(string key) + { + tempStorage.TryGetValue(key, out string? value); + return value; + } + + public async ValueTask<bool> IsPersistent(string key) + { + return false; + } + + public async ValueTask RemoveItem(string key) + { + tempStorage.Remove(key); + } + + public async ValueTask SetItem(string key, string? value, bool persistent = true) + { + if (tempStorage.TryAdd(key, value) is false) + { + tempStorage[key] = value; + } + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/UserService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/UserService.cs new file mode 100644 index 0000000000..fbabf243b3 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Services/UserService.cs @@ -0,0 +1,27 @@ +using Boilerplate.Server.Api.Data; +using Boilerplate.Server.Api.Models.Identity; + +namespace Boilerplate.Tests.Services; + +public partial class UserService(AppDbContext dbContext) +{ + public async Task<User> AddUser(string email) + { + var user = new User + { + EmailConfirmed = true, + UserName = email, + NormalizedUserName = email.ToUpperInvariant(), + Email = email, + NormalizedEmail = email.ToUpperInvariant(), + SecurityStamp = "959ff4a9-4b07-4cc1-8141-c5fc033daf83", + ConcurrencyStamp = "315e1a26-5b3a-4544-8e91-2760cd28e231", + PasswordHash = "AQAAAAIAAYagAAAAEP0v3wxkdWtMkHA3Pp5/JfS+42/Qto9G05p2mta6dncSK37hPxEHa3PGE4aqN30Aag==", // 123456 + }; + + dbContext.Users.Add(user); + await dbContext.SaveChangesAsync(); + + return user; + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestData.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestData.cs new file mode 100644 index 0000000000..d340722353 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestData.cs @@ -0,0 +1,8 @@ +namespace Boilerplate.Tests; + +public partial class TestData +{ + public const string DefaultTestEmail = "test@bitplatform.dev"; + public const string DefaultTestPassword = "123456"; + public const string DefaultTestFullName = "Boilerplate test account"; +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs new file mode 100644 index 0000000000..db15392677 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/TestsInitializer.cs @@ -0,0 +1,101 @@ +//+:cnd:noEmit +using Microsoft.EntityFrameworkCore; +using Boilerplate.Server.Api.Data; +//#if (database == 'Sqlite') +using Microsoft.Data.Sqlite; +//#endif +//#if (advancedTests == true) +using Boilerplate.Tests.PageTests.PageModels.Identity; +using Boilerplate.Tests.Extensions; +using Boilerplate.Client.Web; +//#endif +using Microsoft.Extensions.Hosting; + +namespace Boilerplate.Tests; + +[TestClass] +public partial class TestsInitializer +{ + //#if (advancedTests == true) + public static string AuthenticationState { get; private set; } = null!; + //#endif + + [AssemblyInitialize] + public static async Task Initialize(TestContext testContext) + { + await using var testServer = new AppTestServer(); + + await testServer.Build( + //#if (advancedTests == true) + configureTestConfigurations: configuration => + { + //Run assembly initialization test in BlazorWebAssembly mode to cache .wasm files + configuration["WebAppRender:BlazorMode"] = BlazorWebAppMode.BlazorWebAssembly.ToString(); + } + //#endif + ).Start(); + + await InitializeDatabase(testServer); + + //#if (advancedTests == true) + await InitializeAuthenticationState(testServer, testContext); + //#endif + } + + //#if (database == 'Sqlite') + //SQLite database in in-memory mode only lives as long as at least one connection to it is open + //This connection is required to keep the database alive during the test run. + private static SqliteConnection connection = null!; + //#endif + private static async Task InitializeDatabase(AppTestServer testServer) + { + if (testServer.WebApp.Environment.IsDevelopment()) + { + await using var scope = testServer.WebApp.Services.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); + //#if (database == 'Sqlite') + if (dbContext.Database.ProviderName!.EndsWith("Sqlite", StringComparison.InvariantCulture)) + { + connection = new SqliteConnection(dbContext.Database.GetConnectionString()); + await connection.OpenAsync(); + } + //#endif + await dbContext.Database.MigrateAsync(); + } + } + + //#if (advancedTests == true) + private static async Task InitializeAuthenticationState(AppTestServer testServer, TestContext testContext) + { + var playwrightPage = new PageTest() { TestContext = testContext }; + await playwrightPage.Setup(); + await playwrightPage.BrowserSetup(); + var currentMethodFullName = $"{typeof(TestsInitializer).FullName}.{(nameof(InitializeAuthenticationState))}"; + var options = new BrowserNewContextOptions().EnableVideoRecording(testContext, currentMethodFullName); + var context = await playwrightPage.NewContextAsync(options); + await context.EnableBlazorWasmCaching(); + + var page = await context.NewPageAsync(); + var signinPage = new SignInPage(page, testServer.WebAppServerAddress); + + Assertions.SetDefaultExpectTimeout(30_000); // Extended timeout for initial WebAssembly load and caching + + await signinPage.Open(); + await signinPage.AssertOpen(); + + Assertions.SetDefaultExpectTimeout(10_000); // Standard timeout for subsequent tests + + var signedInPage = await signinPage.SignInWithEmail(); + await signedInPage.AssertSignInSuccess(); + + var state = await page.Context.StorageStateAsync(); + if (string.IsNullOrEmpty(state)) + throw new InvalidOperationException("Authentication state is null or empty."); + + AuthenticationState = state.Replace(testServer.WebAppServerAddress.OriginalString.TrimEnd('/'), "[ServerAddress]"); + + await context.Browser!.CloseAsync(); + await context.Browser!.DisposeAsync(); + } + //#endif +} diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj index 02424d5d0d..6ae9ab3a5a 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj @@ -14,7 +14,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> @@ -24,19 +24,19 @@ <Content Remove="appsettings.json" /> <EmbeddedResource Include="appsettings.json" /> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI.Icons" Version="8.11.0" /> + <PackageReference Include="Bit.BlazorUI" Version="8.12.0" /> + <PackageReference Include="Bit.BlazorUI.Icons" Version="8.12.0" /> <BlazorWebAssemblyLazyLoad Include="Bit.BlazorUI.Icons.wasm" /> - <PackageReference Include="Bit.BlazorUI.Assets" Version="8.11.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.BlazorUI.Assets" Version="8.12.0" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.10" /> <Using Include="System.Net.Http.Json" /> <Using Include="System.Collections.Concurrent" /> @@ -90,11 +90,11 @@ </Target> <Target Name="BuildIsolatedScssFiles" Inputs="@(IsolatedScssFiles)" Outputs="@(IsolatedScssFiles->Replace('.scss', '.css'))"> - <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <Target Name="BuildGlobalScssFiles" Inputs="@(GlobalScssFiles)" Outputs="wwwroot/styles/app.css"> - <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json index c9a2e3f18b..3241913fc0 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj index 6de2ac3734..f5ed83a289 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> @@ -7,22 +7,22 @@ <ItemGroup> <ProjectReference Include="..\Bit.Websites.Careers.Shared\Bit.Websites.Careers.Shared.csproj" /> <ProjectReference Include="..\Bit.Websites.Careers.Client\Bit.Websites.Careers.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" /> <PackageReference Include="AspNetCore.HealthChecks.System" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" /> </ItemGroup> <ItemGroup> diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Components/App.razor b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Components/App.razor index 3ec293773a..69e45f603c 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Components/App.razor +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Components/App.razor @@ -14,7 +14,7 @@ <Link rel="stylesheet" href="_content/Bit.BlazorUI.Icons/styles/bit.blazorui.icons.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <Link rel="stylesheet" href="styles/app.css" /> - <Link rel="stylesheet" href="Bit.Websites.Careers.Client.bundle.scp.css" /> + <Link rel="stylesheet" href="Bit.Websites.Careers.Server.styles.css" /> </head> <body class="bit-blazor-web"> diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Startup/Middlewares.cs b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Startup/Middlewares.cs index a7b22292db..d3e05f5f0b 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Startup/Middlewares.cs +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Startup/Middlewares.cs @@ -3,10 +3,9 @@ using System.Runtime.Loader; using Bit.Websites.Careers.Server.Components; using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Components.Endpoints; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.Net.Http.Headers; namespace Bit.Websites.Careers.Server.Startup; @@ -73,6 +72,8 @@ public static void Use(WebApplication app, IWebHostEnvironment env, IConfigurati app.MapHealthChecksUI(); } + UseSiteMap(app); + app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() @@ -112,4 +113,32 @@ private static void Configure_404_Page(WebApplication app) } }); } + + private static void UseSiteMap(WebApplication app) + { + var urls = Assembly.Load("Bit.Websites.Careers.Client") + .ExportedTypes + .Where(t => typeof(IComponent).IsAssignableFrom(t)) + .SelectMany(t => t.GetCustomAttributes<Microsoft.AspNetCore.Components.RouteAttribute>()) + .Select(r => r.Template) + .ToList(); + + const string siteMapHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset\r\n xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\r\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"; + + app.MapGet("/sitemap.xml", async context => + { + if (siteMap is null) + { + var baseUrl = new Uri(context.Request.GetBaseUrl()); + + siteMap = $"{siteMapHeader}{string.Join(Environment.NewLine, urls.Select(u => $"<url><loc>{new Uri(baseUrl, u)}</loc></url>"))}</urlset>"; + } + + context.Response.Headers.ContentType = "application/xml"; + + await context.Response.WriteAsync(siteMap, context.RequestAborted); + }); + } + + private static string? siteMap; } diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj index 68cbd4646d..35e5fb020d 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj @@ -6,18 +6,18 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> - <PackageReference Include="System.Text.Json" Version="8.0.4" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" PrivateAssets="all" ExcludeAssets="runtime"> + <PackageReference Include="System.Text.Json" Version="8.0.5" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" PrivateAssets="all" ExcludeAssets="runtime"> <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> diff --git a/src/Websites/Careers/src/Directory.Build.props b/src/Websites/Careers/src/Directory.Build.props index 6754562072..8f121b27da 100644 --- a/src/Websites/Careers/src/Directory.Build.props +++ b/src/Websites/Careers/src/Directory.Build.props @@ -1,4 +1,4 @@ -<!-- Generated by bit-dual template v-8.11.0 --> +<!-- Generated by bit-dual template v-8.12.0 --> <Project> <PropertyGroup> <LangVersion>12.0</LangVersion> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj index 7f5587487e..0e70844fd8 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj @@ -14,7 +14,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> @@ -24,20 +24,20 @@ <Content Remove="appsettings.json" /> <EmbeddedResource Include="appsettings.json" /> - <PackageReference Include="Bit.Butil" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI.Icons" Version="8.11.0" /> + <PackageReference Include="Bit.Butil" Version="8.12.0" /> + <PackageReference Include="Bit.BlazorUI" Version="8.12.0" /> + <PackageReference Include="Bit.BlazorUI.Icons" Version="8.12.0" /> <BlazorWebAssemblyLazyLoad Include="Bit.BlazorUI.Icons.wasm" /> - <PackageReference Include="Bit.BlazorUI.Assets" Version="8.11.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.BlazorUI.Assets" Version="8.12.0" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.10" /> <Using Include="System.Net.Http.Json" /> <Using Include="System.Collections.Concurrent" /> @@ -91,11 +91,11 @@ </Target> <Target Name="BuildIsolatedScssFiles" Inputs="@(IsolatedScssFiles)" Outputs="@(IsolatedScssFiles->Replace('.scss', '.css'))"> - <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <Target Name="BuildGlobalScssFiles" Inputs="@(GlobalScssFiles)" Outputs="wwwroot/styles/app.css"> - <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor index 6b5c1a7bea..c88c46e3fa 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor @@ -46,7 +46,7 @@ <section class="page-section download-section"> <div class="page-section-content"> - <BitText Typography="BitTypography.H3" Gutter>4.9M</BitText> + <BitText Typography="BitTypography.H3" Gutter>5.5M</BitText> <hr style="width:100%" /> <a href="https://www.nuget.org/profiles/bit-foundation" target="_blank"> <BitText Typography="BitTypography.H5" Class="blue-txt"> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil07ConsolePage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil07ConsolePage.razor index 0ab5746d71..bdf931f5d8 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil07ConsolePage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil07ConsolePage.razor @@ -555,4 +555,4 @@ </section> </div> -<NavigationButtons Prev="Keyboard" PrevUrl="/butil/keyboard" Next="Storage" NextUrl="/butil/storage" /> +<NavigationButtons Prev="Keyboard" PrevUrl="/butil/keyboard" Next="Notification" NextUrl="/butil/notification" /> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor new file mode 100644 index 0000000000..60e95cbd51 --- /dev/null +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor @@ -0,0 +1,126 @@ +@page "/butil/notification" +@inherits AppComponentBase +@inject Bit.Butil.Notification notification + +<PageOutlet Url="butil/notification" + Title="Notification - Butil" + Description="Notification class of the bit Butil" /> + +<div class="page-container"> + <BitText Typography="BitTypography.H3" Gutter>Notification</BitText> + <br /> + <BitText Typography="BitTypography.Subtitle1" Gutter> + How to use the Notification class of the bit Butil? + </BitText> + <br /> + + <section class="section-card"> + <BitText Typography="BitTypography.H5" Gutter>Usage</BitText> + <div class="section-card-txt"> + To use the browser notification features you need to inject the Bit.Butil.Notification class and use it like this: +<CodeBox HideCopyButton> +@@inject Bit.Butil.Notification notification + +@@code { + await notification.IsSupported(); + await notification.Show("title", new() { Body = "this is body." }); +}</CodeBox> + </div> + </section> + + <section class="section-card"> + <BitText Typography="BitTypography.H5" Gutter>Methods</BitText> + <div class="section-card-txt"> + <br /> + <b>IsSupported</b>: <br /> + Checks if the runtime (browser or web-view) is supporting the Web Notification API. + <br /><br /> + <BitAccordion Title="Sample"> + <BitPivot> + <BitPivotItem HeaderText="Code"> + <CodeBox> + @isSupportedExampleCode + </CodeBox> + </BitPivotItem> + <BitPivotItem HeaderText="Result"> + <br /> + <BitText>Notification supported? [@isNotificationSupported]</BitText> + <br /> + <BitButton OnClick="CheckIsSupported">IsSupported</BitButton> + <br /> + </BitPivotItem> + </BitPivot> + </BitAccordion> + <br /><br /> + + <b>GetPermission</b>: <br /> + Gets the current permission of the Notification API. + (<a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission_static" target="_blank">MDN</a>). + <br /><br /> + <BitAccordion Title="Sample"> + <BitPivot> + <BitPivotItem HeaderText="Code"> + <CodeBox> + @getPermissionExampleCode + </CodeBox> + </BitPivotItem> + <BitPivotItem HeaderText="Result"> + <br /> + <BitText>Current permission state: [@permissionResult]</BitText> + <br /> + <BitButton OnClick="GetCurrentPermissionState">GetPermission</BitButton> + <br /> + </BitPivotItem> + </BitPivot> + </BitAccordion> + <br /><br /> + + <b>RequestPermission</b>: <br /> + Requests permission from the user for the current origin to display notifications. + (<a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static" target="_blank">MDN</a>). + <br /><br /> + <BitAccordion Title="Sample"> + <BitPivot> + <BitPivotItem HeaderText="Code"> + <CodeBox> + @requestPermissionExampleCode + </CodeBox> + </BitPivotItem> + <BitPivotItem HeaderText="Result"> + <br /> + <BitText>Request permission result: [@requestPermissionResult]</BitText> + <br /> + <BitButton OnClick="RequestPermission">RequestPermission</BitButton> + <br /> + </BitPivotItem> + </BitPivot> + </BitAccordion> + <br /><br /> + + <b>Show</b>: <br /> + Requests a native notification to show to the user. + (<a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification" target="_blank">MDN</a>). + <br/> + <b>Note</b>: The Notification api has some limitation on Android and for it to work properly a + Service Worker needs to be registered first. + <br /><br /> + <BitAccordion Title="Sample"> + <BitPivot> + <BitPivotItem HeaderText="Code"> + <CodeBox> + @showExampleCode + </CodeBox> + </BitPivotItem> + <BitPivotItem HeaderText="Result"> + <br /> + <BitButton OnClick="ShowNotification">Show</BitButton> + <br /> + </BitPivotItem> + </BitPivot> + </BitAccordion> + <br /><br /> + </div> + </section> +</div> + +<NavigationButtons Prev="Console" PrevUrl="/butil/console" Next="Storage" NextUrl="/butil/storage" /> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor.cs new file mode 100644 index 0000000000..7eed395f3b --- /dev/null +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor.cs @@ -0,0 +1,88 @@ +using Bit.Butil; + +namespace Bit.Websites.Platform.Client.Pages.Butil; + +public partial class Butil08NotificationPage +{ + private bool? isNotificationSupported; + private async Task CheckIsSupported() + { + isNotificationSupported = await notification.IsSupported(); + } + + private NotificationPermission? permissionResult = null; + private async Task GetCurrentPermissionState() + { + permissionResult = await notification.GetPermission(); + } + + private NotificationPermission? requestPermissionResult = null; + private async Task RequestPermission() + { + requestPermissionResult = await notification.RequestPermission(); + } + + private async Task ShowNotification() + { + await notification.Show("title", new() { Body = "this is body." }); + } + + + + private string isSupportedExampleCode = +@"@inject Bit.Butil.Notification notification + +<BitText>Notification supported? [@isNotificationSupported]</BitText> + +<BitButton OnClick=""CheckIsSupported"">IsSupported</BitButton> + +@code { + private bool? isNotificationSupported; + + private async Task CheckIsSupported() + { + isNotificationSupported = await notification.IsSupported(); + } +}"; + private string getPermissionExampleCode = +@"@inject Bit.Butil.Notification notification + +<BitText>Current permission state: [@permissionResult]</BitText> + +<BitButton OnClick=""GetCurrentPermissionState"">GetPermission</BitButton> + +@code { + private NotificationPermission? permissionResult = null; + + private async Task GetCurrentPermissionState() + { + permissionResult = await notification.GetPermission(); + } +}"; + private string requestPermissionExampleCode = +@"@inject Bit.Butil.Notification notification + +<BitText>Request permission result: [@requestPermissionResult]</BitText> + +<BitButton OnClick=""RequestPermission"">RequestPermission</BitButton> + +@code { + private NotificationPermission? requestPermissionResult = null; + + private async Task RequestPermission() + { + requestPermissionResult = await notification.RequestPermission(); + } +}"; + private string showExampleCode = +@"@inject Bit.Butil.Notification notification + +<BitButton OnClick=""ShowNotification"">Show</BitButton> + +@code { + private async Task ShowNotification() + { + await notification.Show(""title"", new() { Body = ""this is body."" }); + } +}"; +} diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08NotificationPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor similarity index 98% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor index 7aea5c3059..5ac8f8d745 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor @@ -188,4 +188,4 @@ </section> </div> -<NavigationButtons Prev="Console" PrevUrl="/butil/console" Next="Cookie" NextUrl="/butil/cookie" /> +<NavigationButtons Prev="Notification" PrevUrl="/butil/notification" Next="Cookie" NextUrl="/butil/cookie" /> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor.cs similarity index 99% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor.cs index f956c67b40..2cada890ea 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil08StoragePage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil08StoragePage +public partial class Butil09StoragePage { private string? localLength; private string? sessionLength; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09StoragePage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor.cs similarity index 98% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor.cs index f27772ecf2..5eaca45f7a 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil09CookiePage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor.cs @@ -2,7 +2,7 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil09CookiePage +public partial class Butil10CookiePage { private string? newCookieName; private string? newCookieValue; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10CookiePage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor.cs similarity index 98% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor.cs index 6da6bef066..7a35404483 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil10HistoryPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor.cs @@ -2,7 +2,7 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil10HistoryPage +public partial class Butil11HistoryPage { private string? historyLength; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11HistoryPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor.cs similarity index 99% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor.cs index 4bb578d545..cef401b457 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor.cs @@ -2,7 +2,7 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil11ElementPage +public partial class Butil12ElementPage { private string? attributeName; private string? currentAttribute; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil11ElementPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12ElementPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor.cs similarity index 98% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor.cs index 009c8246f9..e960d7663a 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil12WindowPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil12WindowPage +public partial class Butil13WindowPage { private float innerHeight; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13WindowPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor.cs similarity index 94% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor.cs index a00f91c9d2..149dd55de7 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil13DocumentPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor.cs @@ -2,7 +2,7 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil13DocumentPage +public partial class Butil14DocumentPage { private bool isDesignModeOn; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14DocumentPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor.cs similarity index 99% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor.cs index ed73be01dc..898bb396f1 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil14NavigatorPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor.cs @@ -2,7 +2,7 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil14NavigatorPage +public partial class Butil15NavigatorPage { private string? deviceMemory; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15NavigatorPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor.cs similarity index 99% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor.cs index 1b87b79a7d..2770dad9ab 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil15LocationPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil15LocationPage +public partial class Butil16LocationPage { private string? newHref; private string? currentHref; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16LocationPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor.cs similarity index 99% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor.cs index 500f383a06..bbb5f5a7ab 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil16ScreenPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil16ScreenPage +public partial class Butil17ScreenPage { private string? availableHeight; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17ScreenPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor.cs similarity index 98% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor.cs index 474b57e5cb..949a4df1d0 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil17VisualViewportPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil17VisualViewportPage +public partial class Butil18VisualViewportPage { private double offsetLeft; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor.scss similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor.scss rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18VisualViewportPage.razor.scss diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor similarity index 100% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.cs similarity index 95% rename from src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor.cs rename to src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.cs index 5996e142ad..15a39c5b20 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil18ScreenOrientationPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.cs @@ -1,6 +1,6 @@ namespace Bit.Websites.Platform.Client.Pages.Butil; -public partial class Butil18ScreenOrientationPage +public partial class Butil19ScreenOrientationPage { private ushort angle; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.scss b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.scss new file mode 100644 index 0000000000..7cee07def4 --- /dev/null +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Butil/Butil19ScreenOrientationPage.razor.scss @@ -0,0 +1,16 @@ +@import '../../Styles/abstracts/_colors.scss'; +@import '../../Styles/abstracts/_mixins.scss'; +@import '../../Styles/abstracts/_functions.scss'; +@import '../../Styles/abstracts/_media-queries.scss'; + +.page-container { + @include PageContainer; +} + +.section-card { + @include SectionCard; +} + +.section-card-txt { + @include SectionCardText; +} diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates01OverviewPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates01OverviewPage.razor index a09cf263d0..17df98a396 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates01OverviewPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates01OverviewPage.razor @@ -1,5 +1,7 @@ @page "/templates" @page "/templates/overview" +@page "/boilerplate" +@page "/boilerplate/overview" @inherits AppComponentBase <PageOutlet Url="templates/overview" @@ -52,7 +54,7 @@ Blazor Hybrid apps have the following additional platform requirements: </BitText> <ul dir="auto"> - <li>Android 7.1 (API Level 24) or higher</li> + <li>Android 8 (API Level 26) or higher</li> <li>iOS 15 or higher</li> <li>macOS 12 (Monterey) or higher</li> <li>Windows 7 SP1 or higher</li> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates02SamplesPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates02SamplesPage.razor index 75babadfba..13bea41f53 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates02SamplesPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates02SamplesPage.razor @@ -1,4 +1,5 @@ @page "/templates/samples" +@page "/boilerplate/samples" @inherits AppComponentBase <PageOutlet Url="templates/samples" diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor index 007c82f96f..09fcf275c6 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor @@ -1,5 +1,7 @@ @page "/templates/getting-started" @page "/templates/development-prerequisites" +@page "/boilerplate/getting-started" +@page "/boilerplate/development-prerequisites" @inherits AppComponentBase <PageOutlet Url="templates/getting-started" @@ -101,12 +103,11 @@ { <BitAccordion Title=".NET 8 Sdk on Linux instructions"> <div>To install .NET 8 SDK on Linux(Ubuntu) run the following commands:</div> - <CodeBox>wget https://download.visualstudio.microsoft.com/download/pr/db901b0a-3144-4d07-b8ab-6e7a43e7a791/4d9d1b39b879ad969c6c0ceb6d052381/dotnet-sdk-8.0.401-linux-x64.tar.gz -O $HOME/dotnet.tar.gz + <CodeBox>wget https://download.visualstudio.microsoft.com/download/pr/ca6cd525-677e-4d3a-b66c-11348a6f920a/ec395f498f89d0ca4d67d903892af82d/dotnet-sdk-8.0.403-linux-x64.tar.gz -O $HOME/dotnet.tar.gz mkdir -p $HOME/.dotnet tar zxf $HOME/dotnet.tar.gz -C "$HOME/.dotnet" echo 'PATH=$HOME/.dotnet:$HOME/.dotnet/tools:$PATH' >> ~/.bashrc export PATH=$HOME/.dotnet:$HOME/.dotnet/tools:$PATH -dotnet dev-certs https --trust rm $HOME/dotnet.tar.gz</CodeBox> </BitAccordion> } @@ -148,8 +149,8 @@ rm $HOME/dotnet.tar.gz</CodeBox> } </li> <li> - <div style="margin-bottom:8px">Install <a href="https://www.nuget.org/packages/Bit.Boilerplate/8.11.0" target="_blank">Bit Boilerplate</a> project template</div> - <CodeBox>dotnet new install Bit.Boilerplate::8.11.0</CodeBox> + <div style="margin-bottom:8px">Install <a href="https://www.nuget.org/packages/Bit.Boilerplate/8.12.0" target="_blank">Bit Boilerplate</a> project template</div> + <CodeBox>dotnet new install Bit.Boilerplate::8.12.0</CodeBox> </li> @if (showCrossPlatform && devOS is "Windows") { @@ -242,7 +243,6 @@ rm $HOME/dotnet.tar.gz</CodeBox> { <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-maui" title=".NET MAUI" rel="nofollow">.NET MAUI</a></li> } - <li><a href="https://marketplace.visualstudio.com/items?itemName=DominicVonk.vscode-resx-editor" title="RESX Editor" rel="nofollow">RESX Editor</a></li> </ul> </li> </ul> @@ -254,8 +254,7 @@ code --install-extension ms-vscode-remote.remote-containers code --install-extension ms-dotnettools.blazorwasm-companion code --install-extension glenn2223.live-sass code --install-extension kevin-chatham.aspnetcorerazor-html-css-class-completion -code --install-extension ms-dotnettools.dotnet-maui -code --install-extension DominicVonk.vscode-resx-editor</CodeBox> +code --install-extension ms-dotnettools.dotnet-maui</CodeBox> } else { @@ -263,8 +262,7 @@ code --install-extension DominicVonk.vscode-resx-editor</CodeBox> code --install-extension ms-vscode-remote.remote-containers code --install-extension ms-dotnettools.blazorwasm-companion code --install-extension glenn2223.live-sass -code --install-extension kevin-chatham.aspnetcorerazor-html-css-class-completion -code --install-extension DominicVonk.vscode-resx-editor</CodeBox> +code --install-extension kevin-chatham.aspnetcorerazor-html-css-class-completion</CodeBox> } </BitAccordion> </div> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs index 67f1f46f9b..332c7cd297 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs @@ -74,7 +74,7 @@ private async Task CopyCommandsToClipboard() command:"dotnet nuget add source \"https://api.nuget.org/v3/index.json\" --name \"nuget.org\"; dotnet workload install wasm-tools;"), (text:@"echo 'Install the Bit.Boilerplate project template https://www.nuget.org/packages/Boilerplate.Templates';", - command:"dotnet new install Bit.Boilerplate::8.11.0;"), + command:"dotnet new install Bit.Boilerplate::8.12.0;"), ], [CommandGroup.Additional] = @@ -126,9 +126,6 @@ private async Task CopyCommandsToClipboard() (text:@"echo 'Install the .NET MAUI extension for Visual Studio Code https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-maui';", command:"code --install-extension ms-dotnettools.dotnet-maui;"), - - (text:@"echo 'Install the RESX Editor extension for Visual Studio Code https://marketplace.visualstudio.com/items?itemName=DominicVonk.vscode-resx-editor';", - command:"code --install-extension DominicVonk.vscode-resx-editor;"), ], [CommandGroup.Virtualization] = [ diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor index 3343bab688..c5b975bf51 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor @@ -1,4 +1,5 @@ @page "/templates/project-structure" +@page "/boilerplate/project-structure" @inherits AppComponentBase <PageOutlet Url="templates/project-structure" @@ -12,7 +13,7 @@ <div class="section-card-txt"> To explore the structure of your project, start by creating a new project using the following command in the command line: <br /> - <CodeBox>dotnet new bit-bp –-name MyFirstProject</CodeBox> + <CodeBox>dotnet new bit-bp --name MyFirstProject</CodeBox> </div> <div class="row-section-container"> <div> @@ -70,7 +71,7 @@ <br /> <br /> <code> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> + <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> </code> <br /> <br /> @@ -149,24 +150,17 @@ Additionally, it features an auto-update capability. <br /> <br /> - If you set BlazorWebAssemblyStandalone to true in the Directory.Build.props file, the Client.Web project can be published as a + Client.Web project can be published as a Blazor WebAssembly Standalone application on platforms such as <a href="https://pages.cloudflare.com/" target="_blank">Cloudflare Pages</a>, <a href="https://azure.microsoft.com/en-us/products/app-service/static" target="_blank">Azure Static Web Apps</a>, <a href="https://pages.github.com/" target="_blank">GitHub Pages</a>, and others, without requiring ASP.NET Core. - <br /> - <br /> - We recommend keeping BlazorWebAssemblyStandalone set to false during development and only enabling it during the publishing process. - To do this, use the following command: - <br /> - <CodeBox>dotnet publish MyFirstProject.Client.Web -c Release -p:BlazorWebAssemblyStandalone=true</CodeBox> </div> </section> <section class="section-card"> <div class="section-card-txt"> <BitText Typography="BitTypography.H5" Gutter>Test Project</BitText> - A sample test project is provided to demonstrate how to add integration tests to your project in a way that replicates the real - behavior of a server, such as using SignalR. Additionally, it offers guidance on using dependency injection to mock server behavior and logic when needed. + A sample test project is provided to demonstrate how to add integration and UI tests to your project. </div> </section> </div> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor index dca4e7b97a..7e283c80ea 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor @@ -1,4 +1,5 @@ @page "/templates/create-project" +@page "/boilerplate/create-project" @inherits AppComponentBase <PageOutlet Url="templates/create-project" @@ -150,7 +151,7 @@ <BitGridItem Class="grid-item"> <div class="row"> <BitText Typography="BitTypography.H6" Gutter>SignalR</BitText> - <BitToggle @bind-Value="signalr.Value" OnText="true" OffText="false" /> + <BitToggle @bind-Value="signalR.Value" OnText="true" OffText="false" /> </div> <br /> <CodeBox> @@ -217,7 +218,7 @@ Warning: It is advisable to use this option only when necessary, as integrating Entity Framework Core can increase application size and potentially reduce startup performance. </BitGridItem> - <BitGridItem Class="grid-item" ColumnSpan="2"> + <BitGridItem Class="grid-item"> <div class="row"> <BitText Typography="BitTypography.H6" Gutter>Server Database</BitText> <BitDropdown Placeholder="Choose database" Items="@database.Items" @bind-Value="@database.Value" /> @@ -278,6 +279,18 @@ } </BitGridItem> + <BitGridItem Class="grid-item"> + <div class="row"> + <BitText Typography="BitTypography.H6" Gutter>Push Notification (Native and Web Push)</BitText> + <BitToggle @bind-Value="notification.Value" OnText="true" OffText="false" /> + </div> + <br /> + <CodeBox> + @GetNotificationCommand() + </CodeBox> + Setting this parameter to True enables native push notification using firebase and APN for Androiod, iOS and macOS apps alongside with Web Push for Web Browsers. + </BitGridItem> + <BitGridItem Class="grid-item" ColumnSpan="2"> <div class="row"> <BitText Typography="BitTypography.H6" Gutter>API</BitText> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor.cs index 2860b38257..f5867c7114 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates05CreateProjectPage.razor.cs @@ -23,6 +23,12 @@ public partial class Templates05CreateProjectPage Value = false, Default = false, }; + + private Parameter<bool> notification = new() + { + Value = false, + Default = false, + }; private Parameter<bool> appInsight = new() { @@ -30,7 +36,7 @@ public partial class Templates05CreateProjectPage Default = false, }; - private Parameter<bool> signalr = new() + private Parameter<bool> signalR = new() { Value = false, Default = false, @@ -38,8 +44,8 @@ public partial class Templates05CreateProjectPage private Parameter<string> captcha = new() { - Value = "reCaptcha", - Default = "reCaptcha", + Value = "None", + Default = "None", Items = [ new() { Text = "None", Value = "None" }, new() { Text = "reCaptcha", Value = "reCaptcha" }, @@ -152,12 +158,17 @@ private string GetFinalCommand() finalCommand.Append(GetOfflineDbCommand()); } + if (notification.IsModified) + { + finalCommand.Append(GetNotificationCommand()); + } + if (appInsight.IsModified) { finalCommand.Append(GetAppInsightsCommand()); } - if (signalr.IsModified) + if (signalR.IsModified) { finalCommand.Append(GetSignalRCommand()); } @@ -215,6 +226,11 @@ private string GetOfflineDbCommand() return $"--offlineDb {offlineDb.Value.ToString().ToLowerInvariant()} "; } + private string GetNotificationCommand() + { + return $"--notification {notification.Value.ToString().ToLowerInvariant()} "; + } + private string GetAppInsightsCommand() { return $"--appInsights {appInsight.Value.ToString().ToLowerInvariant()} "; @@ -222,7 +238,7 @@ private string GetAppInsightsCommand() private string GetSignalRCommand() { - return $"--signalr {signalr.Value.ToString().ToLowerInvariant()} "; + return $"--signalR {signalR.Value.ToString().ToLowerInvariant()} "; } private class Parameter<T> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor index 75857c41d8..12c8c2cace 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor @@ -1,4 +1,5 @@ @page "/templates/run-project" +@page "/boilerplate/run-project" @inherits AppComponentBase <PageOutlet Url="templates/run-project" @@ -57,28 +58,26 @@ <section class="section-card"> <div class="section-card-txt"> <BitText Typography="BitTypography.H5" Gutter>Blazor Mode Selection</BitText> - To choose between Blazor modes, refer to the Client.Core/Services/AppRenderMode.cs file. By default, for development it - is Blazor Server and for Production it is Blazor Auto. + To choose between Blazor modes, refer to the Client.Web/appsettings.json file. By default, for development it + is Blazor Server and for Production it is Blazor Auto (appsettings.Production.json). <br /> Blazor Auto is selected for production to optimize the user experience. On the first visit, the website will load quickly in Blazor Server mode, while Blazor WebAssembly files are downloaded in the background. Subsequent visits will utilize Blazor WebAssembly, enhancing performance. - <CodeBox>public static IComponentRenderMode? Current => - AppEnvironment.IsDev() - ? BlazorServer // For better development experience. - : BlazorAuto; // For better production experience.</CodeBox> + <CodeBox>"WebAppRender": { + "PrerenderEnabled": false, + "BlazorMode": "BlazorServer" // BlazorServer, BlazorWebAssembly, BlazorAuto +}</CodeBox> <BitText Typography="BitTypography.H5" Gutter>Debugging in Blazor WebAssembly</BitText> To debug the project in Blazor WebAssembly mode, change the Current value to BlazorWebAssembly for development. To fully utilize debugging features, set the Launch profile to Server.Web-BlazorWebAssembly. <br /> - <br /> - <BitText Typography="BitTypography.H5" Gutter>Running in Blazor WebAssembly Standalone Mode</BitText> - To run the project in Blazor WebAssembly Standalone mode, set <i>BlazorWebAssemblyStandalone</i> to true in the Directory.Build.props file. - Additionally, run Client.Web in addition to server project. - <br /> <div class="image-container"> <img class="image" src="images/templates/launch-options.webp" /> </div> + <br /> + <BitText Typography="BitTypography.H5" Gutter>Running in Blazor WebAssembly Standalone Mode</BitText> + To run the project in Blazor WebAssembly Standalone mode, run Client.Web in addition to server project. </div> </section> <br /> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/MainLayout.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/MainLayout.razor.cs index 5fcbb2c865..a46596b656 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/MainLayout.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/MainLayout.razor.cs @@ -29,12 +29,12 @@ public partial class MainLayout : IDisposable private readonly List<BitNavItem> templatesNavItems = [ - new BitNavItem { Text = "Overview", Url = "/templates/overview" }, - new BitNavItem { Text = "Samples", Url = "/templates/samples" }, - new BitNavItem { Text = "Getting started", Url = "/templates/getting-started" }, - new BitNavItem { Text = "Project structure", Url = "/templates/project-structure" }, - new BitNavItem { Text = "Create project", Url = "/templates/create-project" }, - new BitNavItem { Text = "Run project", Url = "/templates/run-project" } + new BitNavItem { Text = "Overview", Url = "/templates", AdditionalUrls = [ "/templates/overview", "/boilerplate", "/boilerplate/overview" ] }, + new BitNavItem { Text = "Samples", Url = "/templates/samples", AdditionalUrls = [ "/boilerplate/samples" ] }, + new BitNavItem { Text = "Getting started", Url = "/templates/getting-started", AdditionalUrls = [ "/templates/development-prerequisites", "/boilerplate/getting-started", "/boilerplate/development-prerequisites" ] }, + new BitNavItem { Text = "Project structure", Url = "/templates/project-structure", AdditionalUrls = [ "/boilerplate/project-structure" ] }, + new BitNavItem { Text = "Create project", Url = "/templates/create-project", AdditionalUrls = [ "/boilerplate/create-project" ] }, + new BitNavItem { Text = "Run project", Url = "/templates/run-project", AdditionalUrls = [ "/boilerplate/run-project" ] } ]; private readonly List<BitNavItem> bswupNavItems = @@ -63,6 +63,7 @@ public partial class MainLayout : IDisposable new BitNavItem { Text = "Clipboard", Url = "/butil/clipboard" }, new BitNavItem { Text = "Keyboard", Url = "/butil/keyboard" }, new BitNavItem { Text = "Console", Url = "/butil/console" }, + new BitNavItem { Text = "Notification", Url = "/butil/notification" }, new BitNavItem { Text = "Storage", Url = "/butil/storage" }, new BitNavItem { Text = "Cookie", Url = "/butil/cookie" }, new BitNavItem { Text = "History", Url = "/butil/history" }, @@ -97,7 +98,7 @@ private void SetNavItems() { var currentUrl = navigationManager.Uri.Replace(navigationManager.BaseUri, "/", StringComparison.InvariantCultureIgnoreCase); - isTemplateDocRoute = currentUrl.Contains("templates") || currentUrl.Contains("admin-panel") || currentUrl.Contains("todo-template"); + isTemplateDocRoute = currentUrl.Contains("templates") || currentUrl.Contains("boilerplate"); isBswupDocRoute = currentUrl.Contains("bswup"); isBesqlDocRoute = currentUrl.Contains("besql"); isButilDocRoute = currentUrl.Contains("butil"); diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/compilerconfig.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/compilerconfig.json index 8a0b46344e..d69139d64b 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/compilerconfig.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/compilerconfig.json @@ -216,68 +216,74 @@ "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil08StoragePage.razor.css", - "inputFile": "Pages/Butil/Butil08StoragePage.razor.scss", + "outputFile": "Pages/Butil/Butil08NotificationPage.razor.css", + "inputFile": "Pages/Butil/Butil08NotificationPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil09CookiePage.razor.css", - "inputFile": "Pages/Butil/Butil09CookiePage.razor.scss", + "outputFile": "Pages/Butil/Butil09StoragePage.razor.css", + "inputFile": "Pages/Butil/Butil09StoragePage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil10HistoryPage.razor.css", - "inputFile": "Pages/Butil/Butil10HistoryPage.razor.scss", + "outputFile": "Pages/Butil/Butil10CookiePage.razor.css", + "inputFile": "Pages/Butil/Butil10CookiePage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil11ElementPage.razor.css", - "inputFile": "Pages/Butil/Butil11ElementPage.razor.scss", + "outputFile": "Pages/Butil/Butil11HistoryPage.razor.css", + "inputFile": "Pages/Butil/Butil11HistoryPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil12WindowPage.razor.css", - "inputFile": "Pages/Butil/Butil12WindowPage.razor.scss", + "outputFile": "Pages/Butil/Butil12ElementPage.razor.css", + "inputFile": "Pages/Butil/Butil12ElementPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil13DocumentPage.razor.css", - "inputFile": "Pages/Butil/Butil13DocumentPage.razor.scss", + "outputFile": "Pages/Butil/Butil13WindowPage.razor.css", + "inputFile": "Pages/Butil/Butil13WindowPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil14NavigatorPage.razor.css", - "inputFile": "Pages/Butil/Butil14NavigatorPage.razor.scss", + "outputFile": "Pages/Butil/Butil14DocumentPage.razor.css", + "inputFile": "Pages/Butil/Butil14DocumentPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil15LocationPage.razor.css", - "inputFile": "Pages/Butil/Butil15LocationPage.razor.scss", + "outputFile": "Pages/Butil/Butil15NavigatorPage.razor.css", + "inputFile": "Pages/Butil/Butil15NavigatorPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil16ScreenPage.razor.css", - "inputFile": "Pages/Butil/Butil16ScreenPage.razor.scss", + "outputFile": "Pages/Butil/Butil16LocationPage.razor.css", + "inputFile": "Pages/Butil/Butil16LocationPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil17VisualViewportPage.razor.css", - "inputFile": "Pages/Butil/Butil17VisualViewportPage.razor.scss", + "outputFile": "Pages/Butil/Butil17ScreenPage.razor.css", + "inputFile": "Pages/Butil/Butil17ScreenPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, { - "outputFile": "Pages/Butil/Butil18ScreenOrientationPage.razor.css", - "inputFile": "Pages/Butil/Butil18ScreenOrientationPage.razor.scss", + "outputFile": "Pages/Butil/Butil18VisualViewportPage.razor.css", + "inputFile": "Pages/Butil/Butil18VisualViewportPage.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, + { + "outputFile": "Pages/Butil/Butil19ScreenOrientationPage.razor.css", + "inputFile": "Pages/Butil/Butil19ScreenOrientationPage.razor.scss", "minify": { "enabled": false }, "options": { "sourceMap": false } }, diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json index 2424b0d7b8..d98bbcbfdb 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/adminpanel.webp b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/adminpanel.webp index b1029f8221..2a9342c51d 100644 Binary files a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/adminpanel.webp and b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/adminpanel.webp differ diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/todotemplate.webp b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/todotemplate.webp index 18670c5b34..38b2a63f2d 100644 Binary files a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/todotemplate.webp and b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/images/templates/todotemplate.webp differ diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj index bad0ca0f40..b2c6f02956 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> @@ -7,22 +7,22 @@ <ItemGroup> <ProjectReference Include="..\Bit.Websites.Platform.Shared\Bit.Websites.Platform.Shared.csproj" /> <ProjectReference Include="..\Bit.Websites.Platform.Client\Bit.Websites.Platform.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" /> <PackageReference Include="AspNetCore.HealthChecks.System" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" /> </ItemGroup> <ItemGroup> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Components/App.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Components/App.razor index 4d46ac2ddf..136bf33140 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Components/App.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Components/App.razor @@ -25,12 +25,12 @@ <Link rel="stylesheet" href="_content/Bit.BlazorUI.Icons/styles/bit.blazorui.icons.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <Link rel="stylesheet" href="styles/app.css" /> - <Link rel="stylesheet" href="Bit.Websites.Platform.Client.bundle.scp.css" /> + <Link rel="stylesheet" href="Bit.Websites.Platform.Server.styles.css" /> </head> <body class="@BitCss.Class.Color.Background.Primary @BitCss.Class.Color.Foreground.Primary bit-blazor-web"> - <!-- Microsoft Clarity --> + <!-- Microsoft Clarity <Script type="text/javascript"> (function (c, l, a, r, i, t, y) { c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments) }; @@ -38,7 +38,7 @@ y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y); })(window, document, "clarity", "script", "ip2k0jz1zu"); </Script> - <!-- Microsoft Clarity --> + Microsoft Clarity --> <Routes @rendermode=RenderModeProvider.Current /> diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Startup/Middlewares.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Startup/Middlewares.cs index e9c30e38dd..0cd9a9f38c 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Startup/Middlewares.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Startup/Middlewares.cs @@ -3,6 +3,7 @@ using System.Runtime.Loader; using Bit.Websites.Platform.Server.Components; using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http.Extensions; @@ -71,6 +72,8 @@ public static void Use(WebApplication app, IWebHostEnvironment env, IConfigurati app.MapHealthChecksUI(); } + UseSiteMap(app); + app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() @@ -110,4 +113,32 @@ private static void Configure_404_Page(WebApplication app) } }); } + + private static void UseSiteMap(WebApplication app) + { + var urls = Assembly.Load("Bit.Websites.Platform.Client") + .ExportedTypes + .Where(t => typeof(IComponent).IsAssignableFrom(t)) + .SelectMany(t => t.GetCustomAttributes<Microsoft.AspNetCore.Components.RouteAttribute>()) + .Select(r => r.Template) + .ToList(); + + const string siteMapHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset\r\n xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\r\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"; + + app.MapGet("/sitemap.xml", async context => + { + if (siteMap is null) + { + var baseUrl = new Uri(context.Request.GetBaseUrl()); + + siteMap = $"{siteMapHeader}{string.Join(Environment.NewLine, urls.Select(u => $"<url><loc>{new Uri(baseUrl, u)}</loc></url>"))}</urlset>"; + } + + context.Response.Headers.ContentType = "application/xml"; + + await context.Response.WriteAsync(siteMap, context.RequestAborted); + }); + } + + private static string? siteMap; } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj index 68cbd4646d..35e5fb020d 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj @@ -6,18 +6,18 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> - <PackageReference Include="System.Text.Json" Version="8.0.4" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" PrivateAssets="all" ExcludeAssets="runtime"> + <PackageReference Include="System.Text.Json" Version="8.0.5" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" PrivateAssets="all" ExcludeAssets="runtime"> <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> diff --git a/src/Websites/Platform/src/Directory.Build.props b/src/Websites/Platform/src/Directory.Build.props index 9b2ca49498..f54fe94aa7 100644 --- a/src/Websites/Platform/src/Directory.Build.props +++ b/src/Websites/Platform/src/Directory.Build.props @@ -1,4 +1,4 @@ -<!-- Generated by bit-dual template v-8.11.0 --> +<!-- Generated by bit-dual template v-8.12.0 --> <Project> <PropertyGroup> <LangVersion>preview</LangVersion> diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj index 4914960cee..96cfd29993 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj @@ -14,7 +14,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> </ItemGroup> <ItemGroup> @@ -24,19 +24,19 @@ <Content Remove="appsettings.json" /> <EmbeddedResource Include="appsettings.json" /> - <PackageReference Include="Bit.BlazorUI" Version="8.11.0" /> - <PackageReference Include="Bit.BlazorUI.Icons" Version="8.11.0" /> + <PackageReference Include="Bit.BlazorUI" Version="8.12.0" /> + <PackageReference Include="Bit.BlazorUI.Icons" Version="8.12.0" /> <BlazorWebAssemblyLazyLoad Include="Bit.BlazorUI.Icons.wasm" /> - <PackageReference Include="Bit.BlazorUI.Assets" Version="8.11.0" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.BlazorUI.Assets" Version="8.12.0" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> + <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.10" /> <Using Include="System.Net.Http.Json" /> <Using Include="System.Collections.Concurrent" /> @@ -90,11 +90,11 @@ </Target> <Target Name="BuildIsolatedScssFiles" Inputs="@(IsolatedScssFiles)" Outputs="@(IsolatedScssFiles->Replace('.scss', '.css'))"> - <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass .:. --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <Target Name="BuildGlobalScssFiles" Inputs="@(GlobalScssFiles)" Outputs="wwwroot/styles/app.css"> - <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=." StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> + <Exec Command="node_modules/.bin/sass Styles/app.scss:wwwroot/styles/app.css --style compressed --load-path=. --silence-deprecation=import" StandardOutputImportance="high" StandardErrorImportance="high" LogStandardErrorAsError="true" /> </Target> <ItemGroup> diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json index cdcfb148ec..524b6e415c 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -28,9 +28,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -418,29 +418,290 @@ "node": ">=18" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/braces": { @@ -448,6 +709,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -456,33 +718,38 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=0.10" } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -493,30 +760,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/fill-range": { @@ -524,6 +791,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -531,55 +799,18 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -589,6 +820,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -601,24 +833,38 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -627,25 +873,28 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -670,6 +919,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -678,9 +928,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json index 794b3f60c3..8f2181ad89 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { - "esbuild": "0.23.1", - "sass": "1.78.0", - "typescript": "5.6.2" + "esbuild": "0.24.0", + "sass": "1.80.5", + "typescript": "5.6.3" } } diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj index 0c4f3285c9..54245f1339 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj @@ -7,22 +7,22 @@ <ItemGroup> <ProjectReference Include="..\Bit.Websites.Sales.Shared\Bit.Websites.Sales.Shared.csproj" /> <ProjectReference Include="..\Bit.Websites.Sales.Client\Bit.Websites.Sales.Client.csproj" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" /> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" /> <PackageReference Include="AspNetCore.HealthChecks.System" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" /> <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" /> </ItemGroup> <ItemGroup> diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor index a8d075c6b6..3b99fd9576 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Components/App.razor @@ -35,7 +35,7 @@ <Link rel="stylesheet" href="_content/Bit.BlazorUI.Icons/styles/bit.blazorui.icons.css" /> <Link rel="stylesheet" href="_content/Bit.BlazorUI.Assets/styles/bit.blazorui.assets.css" /> <Link rel="stylesheet" href="styles/app.css" /> - <Link rel="stylesheet" href="Bit.Websites.Sales.Client.bundle.scp.css" /> + <Link rel="stylesheet" href="Bit.Websites.Sales.Server.styles.css" /> </head> <body class="bit-blazor-web"> diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Startup/Middlewares.cs b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Startup/Middlewares.cs index 435863c48a..bc4433fb00 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Startup/Middlewares.cs +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Startup/Middlewares.cs @@ -2,11 +2,10 @@ using HealthChecks.UI.Client; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.Net.Http.Headers; using Bit.Websites.Sales.Server.Components; using System.Net; -using Microsoft.AspNetCore.Components.Endpoints; using System.Runtime.Loader; +using Microsoft.AspNetCore.Components; namespace Bit.Websites.Sales.Server.Startup; @@ -73,6 +72,8 @@ public static void Use(WebApplication app, IWebHostEnvironment env, IConfigurati app.MapHealthChecksUI(); } + UseSiteMap(app); + app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() @@ -112,4 +113,32 @@ private static void Configure_404_Page(WebApplication app) } }); } + + private static void UseSiteMap(WebApplication app) + { + var urls = Assembly.Load("Bit.Websites.Sales.Client") + .ExportedTypes + .Where(t => typeof(IComponent).IsAssignableFrom(t)) + .SelectMany(t => t.GetCustomAttributes<Microsoft.AspNetCore.Components.RouteAttribute>()) + .Select(r => r.Template) + .ToList(); + + const string siteMapHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset\r\n xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\r\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">"; + + app.MapGet("/sitemap.xml", async context => + { + if (siteMap is null) + { + var baseUrl = new Uri(context.Request.GetBaseUrl()); + + siteMap = $"{siteMapHeader}{string.Join(Environment.NewLine, urls.Select(u => $"<url><loc>{new Uri(baseUrl, u)}</loc></url>"))}</urlset>"; + } + + context.Response.Headers.ContentType = "application/xml"; + + await context.Response.WriteAsync(siteMap, context.RequestAborted); + }); + } + + private static string? siteMap; } diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj index 68cbd4646d..35e5fb020d 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj @@ -6,18 +6,18 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Bit.CodeAnalyzers" Version="8.11.0"> + <PackageReference Include="Bit.CodeAnalyzers" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Bit.SourceGenerators" Version="8.11.0"> + <PackageReference Include="Bit.SourceGenerators" Version="8.12.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> - <PackageReference Include="System.Text.Json" Version="8.0.4" /> - <PackageReference Include="Riok.Mapperly" Version="3.6.0" PrivateAssets="all" ExcludeAssets="runtime"> + <PackageReference Include="System.Text.Json" Version="8.0.5" /> + <PackageReference Include="Riok.Mapperly" Version="4.1.0" PrivateAssets="all" ExcludeAssets="runtime"> <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> diff --git a/src/Websites/Sales/src/Directory.Build.props b/src/Websites/Sales/src/Directory.Build.props index 90db270346..c1389de24f 100644 --- a/src/Websites/Sales/src/Directory.Build.props +++ b/src/Websites/Sales/src/Directory.Build.props @@ -1,4 +1,4 @@ -<!-- Generated by bit-dual template v-8.11.0 --> +<!-- Generated by bit-dual template v-8.12.0 --> <Project> <PropertyGroup> <LangVersion>12.0</LangVersion> diff --git a/src/global.json b/src/global.json index 495d811189..a10ccd6b1d 100644 --- a/src/global.json +++ b/src/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.401", + "version": "8.0.403", "rollForward": "disable" } } \ No newline at end of file