diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c6823b9..7e3587db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,6 @@ jobs: uses: actions/checkout@v7 with: fetch-depth: 0 - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} - name: Setup PowerShell modules run: | @@ -36,33 +35,12 @@ jobs: Install-Module PSSharedGoods -Force -Scope CurrentUser -AllowClobber shell: pwsh - - name: Refresh module manifest - env: - RefreshPSD1Only: 'true' + - name: Validate module manifest run: | - ./Build/Build-Module.ps1 + Test-ModuleManifest -Path ./PSWriteHTML.psd1 -ErrorAction Stop shell: pwsh - - name: Commit refreshed PSD1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HEAD_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref_name }} - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add PSWriteHTML.psd1 - git diff --cached --quiet - if ($LASTEXITCODE -eq 0) { - Write-Host "No PSD1 changes to commit." - } elseif ($LASTEXITCODE -eq 1) { - git commit -m "chore: regenerate psd1" - git push origin HEAD:$Env:HEAD_BRANCH - } else { - throw "Unable to inspect staged PSD1 changes (git exit code $LASTEXITCODE)." - } - shell: pwsh - - - name: Upload refreshed manifest + - name: Upload validated manifest uses: actions/upload-artifact@v7 with: name: psd1 diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 13f3ef8a..ae144b09 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -4,6 +4,19 @@ Unfortunetly some code is very old, some code doesn't serve it's purpose anymore, and requires heavy changes that may have an impact on already deployed code. Please make sure to read changes before updating, as we're undergoing some breaking changes. +## 1.42.0 + +- Table exports support explicit row scopes with `-ExportScope`. Excel retains long numeric identifiers as text and supports named `-TextColumns`. +- CSV exports quote fields and protect formula-like text. `New-TableButtonCSV -RawData` retains those values for programmatic consumers. +- Column filters preserve header names, restore saved searches, and follow column visibility. Search panes support named column selection with `-SearchPaneColumns`. +- Row grouping supports multiple levels, independent collapse state, and responsive child rows. +- Inline table conditions can read excluded helper properties, and table rendering preserves the caller's arrays and dates. +- Nested tabs initialize their tables correctly and support deep links and keyboard navigation. Top menu headings follow their configured destinations. +- Markdown preserves backslashes and multiline content, embeds relative local images, and supports `-ImageBase` and `-EmbedImages`. +- Text links support parenthesized labels and nested parentheses in URLs. Merged table titles and column names support independent alignment. +- Chart and widget serialization preserves quoted strings and keeps data containing HTML script delimiters inside its script value. Ajax table files use UTF-8 on both PowerShell runtimes. +- Selected-row hover backgrounds apply to odd and even rows. + ## 1.36.0 ### What's new - Improvements to null handlings in New-HTMLTable @@ -1170,4 +1183,4 @@ New-HTML -TitleText 'Title' -Online -FilePath $PSScriptRoot\Example-TableColumnV ## 0.0.33 - 2019.05.28 - New-HTMLTable - Sorting ordered for DateTime - PR by Ferdi Weiner (ferwe) - - Removed New-HTMLChart temporary as it will be rebuilt \ No newline at end of file + - Removed New-HTMLChart temporary as it will be rebuilt diff --git a/Private/ChartsAll/New-ApexChart.ps1 b/Private/ChartsAll/New-ApexChart.ps1 index 02307d07..7c2ce5a7 100644 --- a/Private/ChartsAll/New-ApexChart.ps1 +++ b/Private/ChartsAll/New-ApexChart.ps1 @@ -1,4 +1,4 @@ -function New-ApexChart { +function New-ApexChart { <# .SYNOPSIS Creates a new ApexChart with the specified options and events. @@ -53,19 +53,19 @@ function New-ApexChart { # Convert Dictionary to JSON and return chart within SCRIPT tag # Make sure to return with additional empty string Remove-EmptyValue -Hashtable $Options -Recursive -Rerun 2 - $JSON = $Options | ConvertTo-JsonLiteral -Depth 5 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' } - # replaces stuff for TImeLineCharts - $JSON = $JSON.Replace('"new Date(', 'new Date(').Replace(').getTime()"', ').getTime()') + $JSON = $Options | ConvertTo-HTMLScriptJson -Depth 20 # We replace Events on JSON because there's no other way that I can think of if ($Options.chart.events) { - $JSON = $JSON -replace ('"events":"EventsReplaceMe"', $Events) + $JSON = [regex]::Replace($JSON, '"events"\s*:\s*"EventsReplaceMe"', { $Events }) } - $JSON = $JSON | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } - "var options = $JSON" - "var chart = new ApexCharts(document.querySelector('#$ID'), + # Timeline producers return date expressions as data. Parse their date + # argument without evaluating text or unescaping the whole JSON object. + 'if (options.chart.type === "rangeBar") { options.series.forEach(function (series) { series.data.forEach(function (point) { if (Array.isArray(point.y)) { point.y = point.y.map(function (value) { var match = typeof value === "string" && /^new Date\(''([\s\S]*)''\)\.getTime\(\)$/.exec(value); return match ? new Date(match[1]).getTime() : value; }); } }); }); }' + $IDJson = ConvertTo-HTMLScriptJson -InputObject $Options.chart.id -Compress + "var chart = new ApexCharts(document.getElementById($IDJson), options );" "chart.render();" @@ -73,4 +73,4 @@ function New-ApexChart { $Div # we need to move it to the end of the code therefore using additional vesel $Script:HTMLSchema.Charts.Add($Script) -} \ No newline at end of file +} diff --git a/Private/ConvertFrom-HTMLTextLink.ps1 b/Private/ConvertFrom-HTMLTextLink.ps1 new file mode 100644 index 00000000..837756b9 --- /dev/null +++ b/Private/ConvertFrom-HTMLTextLink.ps1 @@ -0,0 +1,13 @@ +function ConvertFrom-HTMLTextLink { + <# + .SYNOPSIS + Converts inline text links with independently captured labels and balanced destinations. + #> + [CmdletBinding()] + param([AllowEmptyString()][string] $Text) + $Pattern = '\[(?