From 1c09f61cf69d5a7de8967acd7f1ae9be6556cb1a Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Sun, 15 Feb 2026 16:43:10 +0000 Subject: [PATCH] #427, #438, #621: Refactor for how Radio/Checkbox options are created (same as Select) - allowing support for finer control over option states, updating values, dynamic options, and much more. Radios also finally have Actions --- docs/Getting-Started/Migrating/08-to-10.md | 74 ++- docs/Tutorials/Actions/Checkbox.md | 172 +++++- docs/Tutorials/Actions/Datalist.md | 8 +- docs/Tutorials/Actions/Error.md | 6 +- docs/Tutorials/Actions/Form.md | 12 +- docs/Tutorials/Actions/Radio.md | 196 ++++++ docs/Tutorials/Actions/Select.md | 6 +- docs/Tutorials/Elements/Checkbox.md | 77 ++- docs/Tutorials/Elements/Form.md | 10 +- docs/Tutorials/Elements/Modal.md | 2 +- docs/Tutorials/Elements/Radio.md | 64 +- docs/Tutorials/Elements/Select.md | 4 +- examples/checkboxes.ps1 | 164 +++++ examples/input_events.ps1 | 8 +- examples/inputs.ps1 | 18 +- examples/radios.ps1 | 98 +++ src/Private/Helpers.ps1 | 3 +- src/Public/Actions.ps1 | 668 +++++++++++++++++++-- src/Public/Elements.ps1 | 307 ++++++++-- src/Templates/Public/scripts/templates.js | 399 ++++++++---- src/Templates/Public/styles/default.css | 25 +- 21 files changed, 2031 insertions(+), 290 deletions(-) create mode 100644 docs/Tutorials/Actions/Radio.md create mode 100644 examples/checkboxes.ps1 create mode 100644 examples/radios.ps1 diff --git a/docs/Getting-Started/Migrating/08-to-10.md b/docs/Getting-Started/Migrating/08-to-10.md index 87727224..16dd2f5f 100644 --- a/docs/Getting-Started/Migrating/08-to-10.md +++ b/docs/Getting-Started/Migrating/08-to-10.md @@ -178,11 +178,81 @@ To assist with most migrations there is a helper function: `ConvertTo-PodeWebOpt ```powershell New-PodeWebSelect -Name 'Example' -Options @( - @('Option1', 'Option2') | ConvertTo-PodeWebOption -SelectedOption 'Option1' + 'Option1', 'Option2' | ConvertTo-PodeWebOption -SelectedOption 'Option1' ) ``` -The `Update-PodeWebSelect` action sees the same update as well. Additionally the `-Value` on `Set-PodeWebSelect` is now `-OptionName`. +The `Update-PodeWebSelect` action sees the same update as well. + +Additionally `Set-PodeWebSelect` is now `Select-PodeWebSelectOption`, and the `-Value` parameter is now `-OptionName`. + +Furthermore, new Actions have been created to Add and Remove Select options; and a new Datalist element has been created as well. + +### Checkboxes + +Checkboxes have seen a similar update to Selects; originally you would create a Checkbox via `New-PodeWebCheckbox` and supply `-Options` as a string array, and optionally `-DisplayOptions`. + +Now, you can create Options via the new `New-PodeWebOption` function. The `-Options` on `New-PodeWebCheckbox` now accepts an array of these elements, and the `-DisplayOptions` parameter has moved to `New-PodeWebOption` as `-DisplayName`. Additionally, while an Alias does exist, `-Checked` has been renamed to `-Selected`. + +For example, the following previous Checkbox element: + +```powershell +New-PodeWebCheckbox -Name 'Example' -Options @('Option1', 'Option2') +``` + +Would now be the following instead: + +```powershell +New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Selected + New-PodeWebOption -Name 'Option2' +) +``` + +To assist with most migrations there is a helper function: `ConvertTo-PodeWebOption`. You can pipe an original raw string array into this, and it will convert to strings into Option elements for you: + +```powershell +New-PodeWebCheckbox -Name 'Example' -Options @( + 'Option1', 'Option2' | ConvertTo-PodeWebOption -SelectedOption 'Option1' +) +``` + +The `Update-PodeWebCheckbox` action sees the same update as well - where `-OptionId` is now `-Options`, and can be used to overwrite all options for a Checkbox (like you can with a Select) + +Additionally `Enable-PodeWebCheckbox` and `Disable-PodeWebCheckbox` have had their `-OptionId` renamed to `-OptionName`. Instead of referencing an option by its "index", you can now just reference it by the `-Name` supplied to `New-PodeWebOption`. + +Furthermore, new Actions have been created to Add, Remove, Select, Reset, and Clear Checkbox options. + +### Radios + +Radio elements have seen a similar update to Selects and Checkboxes; originally you would create a Radio via `New-PodeWebRadio` and supply `-Options` as a string array, and optionally `-DisplayOptions`. + +Now, you can create Options via the new `New-PodeWebOption` function. The `-Options` on `New-PodeWebRadio` now accepts an array of these elements, and the `-DisplayOptions` parameter has moved to `New-PodeWebOption` as `-DisplayName`. + +For example, the following previous Radio element: + +```powershell +New-PodeWebRadio -Name 'Example' -Options @('Option1', 'Option2') +``` + +Would now be the following instead: + +```powershell +New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Selected + New-PodeWebOption -Name 'Option2' +) +``` + +To assist with most migrations there is a helper function: `ConvertTo-PodeWebOption`. You can pipe an original raw string array into this, and it will convert to strings into Option elements for you: + +```powershell +New-PodeWebRadio -Name 'Example' -Options @( + 'Option1', 'Option2' | ConvertTo-PodeWebOption -SelectedOption 'Option1' +) +``` + +Furthermore, the Radio elements finally have Update, Add, Remove, Select, etc. Actions - akin to Checkboxes. ## Classes and Styles diff --git a/docs/Tutorials/Actions/Checkbox.md b/docs/Tutorials/Actions/Checkbox.md index c39d6f68..d34281bb 100644 --- a/docs/Tutorials/Actions/Checkbox.md +++ b/docs/Tutorials/Actions/Checkbox.md @@ -2,16 +2,68 @@ This page details the actions available to Checkboxes. +## Add + +To add one or more Options you can use [`Add-PodeWebCheckboxOption`](../../../Functions/Actions/Add-PodeWebCheckboxOption) along with `-Option`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Add Option' -ScriptBlock { + Add-PodeWebCheckboxOption -Name 'Example' -Option @( + New-PodeWebOption -Name 'Added1' + ) + } +) +``` + +## Clear + +To clear the options of a Checkbox element, you can use [`Clear-PodeWebCheckbox`](../../../Functions/Actions/Clear-PodeWebCheckbox): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Clear Checkbox' -ScriptBlock { + Clear-PodeWebCheckbox -Name 'Example' + } +) +``` + ## Disable -To enable a checkbox you can use [`Disable-PodeWebCheckbox`](../../../Functions/Actions/Disable-PodeWebCheckbox): +To disable a checkbox you can use [`Disable-PodeWebCheckbox`](../../../Functions/Actions/Disable-PodeWebCheckbox): ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebCheckbox -Name 'Enable' -AsSwitch + New-PodeWebCheckbox -Name 'Example' -AsSwitch New-PodeWebButton -Name 'Disable Checkbox' -ScriptBlock { - Disable-PodeWebCheckbox -Name 'Enable' + Disable-PodeWebCheckbox -Name 'Example' + } +) +``` + +For a Checkbox with single or multiple options, this will disable all options. For a Checkbox with multiple options you can specify which option to disable using the `-OptionName` parameter: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Disable Option1' -ScriptBlock { + Disable-PodeWebCheckbox -Name 'Example' -OptionName 'Option1' } ) ``` @@ -22,31 +74,121 @@ To enable a checkbox you can use [`Enable-PodeWebCheckbox`](../../../Functions/A ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebCheckbox -Name 'Disabled' -AsSwitch -Disabled + New-PodeWebCheckbox -Name 'Example' -AsSwitch -Disabled New-PodeWebButton -Name 'Enable Checkbox' -ScriptBlock { - Enable-PodeWebCheckbox -Name 'Disabled' + Enable-PodeWebCheckbox -Name 'Example' } ) ``` -## Update +For a Checkbox with single or multiple options, this will enable all options. For a Checkbox with multiple options you can specify which option to enable using the `-OptionName` parameter: -To update a checkbox to be checked/unchecked, or to enable/disable, you can use [`Update-PodeWebCheckbox`](../../../Functions/Actions/Update-PodeWebCheckbox): +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Disabled + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Enable Option1' -ScriptBlock { + Enable-PodeWebCheckbox -Name 'Example' -OptionName 'Option1' + } +) +``` + +## Remove + +To remove one or more Options you can use [`Remove-PodeWebCheckboxOption`](../../../Functions/Actions/Remove-PodeWebCheckboxOption) along with `-OptionName`: ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebCheckbox -Name 'Enabled' -AsSwitch + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) - New-PodeWebButton -Name 'Update Checkbox' -ScriptBlock { - $checked = [bool](Get-Random -Minimum 0 -Maximum 2) - Update-PodeWebCheckbox -Name 'Enabled' -Checked:$checked + New-PodeWebButton -Name 'Remove Options' -ScriptBlock { + Remove-PodeWebCheckboxOption -Name 'Example' -OptionName 'Option1', 'Option3' } - New-PodeWebButton -Name 'Enable Checkbox' -ScriptBlock { - Update-PodeWebCheckbox -Name 'Enabled' -Checked:$false -State Enabled +) +``` + +## Reset + +To reset/deselect options of a Checkbox element, you can use [`Reset-PodeWebCheckbox`](../../../Functions/Actions/Reset-PodeWebCheckbox): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Selected + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Deselect Option' -ScriptBlock { + Reset-PodeWebCheckbox -Name 'Example' -OptionName 'Option1' } - New-PodeWebButton -Name 'Disable Checkbox' -ScriptBlock { - Update-PodeWebCheckbox -Name 'Enabled' -Checked:$false -State Disabled +) +``` + +## Select + +To select options of a Checkbox element, you can use [`Select-PodeWebCheckbox`](../../../Functions/Actions/Select-PodeWebCheckbox): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Select Option' -ScriptBlock { + Select-PodeWebCheckbox -Name 'Example' -OptionName 'Option2' + } +) +``` + +## Sync + +If you built a Checkbox element with the `-ScriptBlock` parameter, then you can re-invoke the scriptblock to update the element by using [`Sync-PodeWebCheckbox`](../../../Functions/Actions/Sync-PodeWebCheckbox): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheck -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButton -Name 'Sync Checkbox' -ScriptBlock { + Sync-PodeWebCheckbox -Name 'Example' + } +) +``` + +## Update + +You can update a Checkbox element's options by using [`Update-PodeWebCheckbox`](../../../Functions/Actions/Update-PodeWebCheckbox): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheckbox -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButton -Name 'New Options' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + }) + + $options | + ConvertTo-PodeWebOption | + Update-PodeWebCheckbox -Name 'Example' } ) ``` diff --git a/docs/Tutorials/Actions/Datalist.md b/docs/Tutorials/Actions/Datalist.md index 73794457..577acc63 100644 --- a/docs/Tutorials/Actions/Datalist.md +++ b/docs/Tutorials/Actions/Datalist.md @@ -57,9 +57,9 @@ New-PodeWebContainer -NoBackground -Content @( ) ``` -## Set +## Select -To set the currently selected option/value of a Datalist element, you can use [`Set-PodeWebDatalist`](../../../Functions/Actions/Set-PodeWebDatalist). You can either set the value to a predefined option, or to a custom value not in the options list: +To set the currently selected option/value of a Datalist element, you can use [`Select-PodeWebDatalistOption`](../../../Functions/Actions/Select-PodeWebDatalistOption). You can either set the value to a predefined option, or to a custom value not in the options list: ```powershell New-PodeWebContainer -NoBackground -Content @( @@ -72,11 +72,11 @@ New-PodeWebContainer -NoBackground -Content @( New-PodeWebButton -Name 'Update Datalist Predefined' -ScriptBlock { $rand = Get-Random -Minimum 0 -Maximum 3 $opt = (@('Option1', 'Option2', 'Option3'))[$rand] - Set-PodeWebDatalist -Name 'Example' -Value $opt + Select-PodeWebDatalistOption -Name 'Example' -Value $opt } New-PodeWebButton -Name 'Update Datalist Custom' -ScriptBlock { - Set-PodeWebDatalist -Name 'Example' -Value 'Custom Value' + Select-PodeWebDatalistOption -Name 'Example' -Value 'Custom Value' } ) ``` diff --git a/docs/Tutorials/Actions/Error.md b/docs/Tutorials/Actions/Error.md index 246b5e2f..4abe6ac4 100644 --- a/docs/Tutorials/Actions/Error.md +++ b/docs/Tutorials/Actions/Error.md @@ -13,9 +13,11 @@ New-PodeWebCard -Content @( } -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock - New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch + New-PodeWebCheckbox -Name 'Checkboxes' -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) -AsSwitch New-PodeWebSelect -Name 'Role' -Multiple -Options @( - @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + 'User', 'Admin', 'Operations' | ConvertTo-PodeWebOption ) ) ) diff --git a/docs/Tutorials/Actions/Form.md b/docs/Tutorials/Actions/Form.md index b03c5902..776b9722 100644 --- a/docs/Tutorials/Actions/Form.md +++ b/docs/Tutorials/Actions/Form.md @@ -11,9 +11,11 @@ New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock {} -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock - New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch + New-PodeWebCheckbox -Name 'Checkboxes' -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) -AsSwitch New-PodeWebSelect -Name 'Role' -Multiple -Options @( - @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + 'User', 'Admin', 'Operations' | ConvertTo-PodeWebOption ) ) ) @@ -34,9 +36,11 @@ New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock {} -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock - New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch + New-PodeWebCheckbox -Name 'Checkboxes' -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) -AsSwitch New-PodeWebSelect -Name 'Role' -Multiple -Options @( - @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + 'User', 'Admin', 'Operations' | ConvertTo-PodeWebOption ) ) ) diff --git a/docs/Tutorials/Actions/Radio.md b/docs/Tutorials/Actions/Radio.md new file mode 100644 index 00000000..2fa31b71 --- /dev/null +++ b/docs/Tutorials/Actions/Radio.md @@ -0,0 +1,196 @@ +# Radio + +This page details the actions available to Radio elements. + +## Add + +To add one or more Options you can use [`Add-PodeWebRadioOption`](../../../Functions/Actions/Add-PodeWebRadioOption) along with `-Option`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Add Option' -ScriptBlock { + Add-PodeWebRadioOption -Name 'Example' -Option @( + New-PodeWebOption -Name 'Added1' + ) + } +) +``` + +## Clear + +To clear the options of a Radio element, you can use [`Clear-PodeWebRadio`](../../../Functions/Actions/Clear-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Clear Radio' -ScriptBlock { + Clear-PodeWebRadio -Name 'Example' + } +) +``` + +## Disable + +To disable a Radio you can use [`Disable-PodeWebRadio`](../../../Functions/Actions/Disable-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -AsSwitch + + New-PodeWebButton -Name 'Disable Radio' -ScriptBlock { + Disable-PodeWebRadio -Name 'Example' + } +) +``` + +If no Option name is supplied this will disable all options, or you can specify which option to disable using the `-OptionName` parameter: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Disable Option1' -ScriptBlock { + Disable-PodeWebRadio -Name 'Example' -OptionName 'Option1' + } +) +``` + +## Enable + +To enable a Radio you can use [`Enable-PodeWebRadio`](../../../Functions/Actions/Enable-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -AsSwitch -Disabled + + New-PodeWebButton -Name 'Enable Radio' -ScriptBlock { + Enable-PodeWebRadio -Name 'Example' + } +) +``` + +If no Option name is supplied this will enable all options, or you can specify which option to enable using the `-OptionName` parameter: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Disabled + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Enable Option1' -ScriptBlock { + Enable-PodeWebRadio -Name 'Example' -OptionName 'Option1' + } +) +``` + +## Remove + +To remove one or more Options you can use [`Remove-PodeWebRadioOption`](../../../Functions/Actions/Remove-PodeWebRadioOption) along with `-OptionName`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Remove Options' -ScriptBlock { + Remove-PodeWebRadioOption -Name 'Example' -OptionName 'Option1', 'Option3' + } +) +``` + +## Reset + +To reset/deselect and option of a Radio element, you can use [`Reset-PodeWebRadio`](../../../Functions/Actions/Reset-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' -Selected + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Deselect Option' -ScriptBlock { + Reset-PodeWebRadio -Name 'Example' -OptionName 'Option1' + } +) +``` + +## Select + +To select an option of a Radio element, you can use [`Select-PodeWebRadio`](../../../Functions/Actions/Select-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Select Option' -ScriptBlock { + Select-PodeWebRadio -Name 'Example' -OptionName 'Option2' + } +) +``` + +## Sync + +If you built a Radio element with the `-ScriptBlock` parameter, then you can re-invoke the scriptblock to update the element by using [`Sync-PodeWebRadio`](../../../Functions/Actions/Sync-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebCheck -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButton -Name 'Sync Radio' -ScriptBlock { + Sync-PodeWebRadio -Name 'Example' + } +) +``` + +## Update + +You can update a Radio element's options by using [`Update-PodeWebRadio`](../../../Functions/Actions/Update-PodeWebRadio): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebRadio -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButton -Name 'New Options' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + }) + + $options | + ConvertTo-PodeWebOption | + Update-PodeWebRadio -Name 'Example' + } +) +``` + +Various other properties can be updated as well. diff --git a/docs/Tutorials/Actions/Select.md b/docs/Tutorials/Actions/Select.md index c7cdb04a..70ef3865 100644 --- a/docs/Tutorials/Actions/Select.md +++ b/docs/Tutorials/Actions/Select.md @@ -81,9 +81,9 @@ New-PodeWebContainer -NoBackground -Content @( ) ``` -## Set +## Select -To set the currently selected option/value of a Select element, you can use [`Set-PodeWebSelect`](../../../Functions/Actions/Set-PodeWebSelect): +To set the currently selected option/value of a Select element, you can use [`Select-PodeWebSelectOption`](../../../Functions/Actions/Select-PodeWebSelectOption): ```powershell New-PodeWebContainer -NoBackground -Content @( @@ -96,7 +96,7 @@ New-PodeWebContainer -NoBackground -Content @( New-PodeWebButton -Name 'Update Select' -ScriptBlock { $rand = Get-Random -Minimum 0 -Maximum 3 $opt = (@('Option1', 'Option2', 'Option3'))[$rand] - Set-PodeWebSelect -Name 'Example' -OptionName $opt + Select-PodeWebSelectOption -Name 'Example' -OptionName $opt } ) ``` diff --git a/docs/Tutorials/Elements/Checkbox.md b/docs/Tutorials/Elements/Checkbox.md index ea0a745a..9c8c9718 100644 --- a/docs/Tutorials/Elements/Checkbox.md +++ b/docs/Tutorials/Elements/Checkbox.md @@ -1,10 +1,16 @@ # Checkbox -| Support | | -| ------- |-| -| Events | Yes | +| Support | | +| ------- | --- | +| Events | Yes | -The Checkbox element is a form input element, and can be added using [`New-PodeWebCheckbox`](../../../Functions/Elements/New-PodeWebCheckbox). This will add a checkbox to your form, and you can render with checkbox as a switch using `-AsSwitch`: +The Checkbox element is a form input element, and can be added using [`New-PodeWebCheckbox`](../../../Functions/Elements/New-PodeWebCheckbox). This will add a checkbox to your form, and you can render with checkbox as a switch using `-AsSwitch`. + +## Options + +### Single + +To create a Checkbox element with a single option, you simply need to supply just the `-Name`: ```powershell New-PodeWebCard -Content @( @@ -13,25 +19,31 @@ New-PodeWebCard -Content @( $enable = $WebEvent.Data['Enable'] } -Content @( New-PodeWebCheckbox -Name 'Accepts Terms' - New-PodeWebCheckbox -Name 'Enable' -Checked -AsSwitch + New-PodeWebCheckbox -Name 'Enable' -Selected -AsSwitch ) ) ``` -When using singular checkboxes like above, the value in `$WebEvent` will be `true` or `false` strings. - -Which looks like below: +When using single option checkboxes, the value in `$WebEvent.Data` value will be a `true` string - or omitted for deselected Checkboxes. ![checkbox](../../../images/checkbox.png) -You can also setup a checkbox to have multiple options like below; in this case, the value will be a comma separated list of the selected options: +### Multiple + +To create a Checkbox element with multiple pre-defined options, you can use the `-Options` parameter which accepts an array of [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption): ```powershell New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock { $langs = $WebEvent.Data['Spoken Languages'] } -Content @( - New-PodeWebCheckbox -Name 'Spoken Languages' -Options 'English', 'French', 'Japanese', 'Chinese', 'Other' -AsSwitch + New-PodeWebCheckbox -Name 'Spoken Languages' -AsSwitch -Options @( + New-PodeWebOption -Name 'English' -Selected + New-PodeWebOption -Name 'French' + New-PodeWebOption -Name 'Japanese' + New-PodeWebOption -Name 'Chinese' + New-PodeWebOption -Name 'Other' + ) ) ) ``` @@ -40,10 +52,49 @@ Which looks like below: ![checkbox_multi](../../../images/checkbox_multi.png) +### Dynamic + +You can build a Checkbox element's options dynamically by using the `-ScriptBlock` parameter. This will allow you to retrieve the options from elsewhere for use with the Checkbox element. + +You can either return an array of raw string values, or pipe the options into, and return, [`Update-PodeWebCheckbox`](../../../Functions/Actions/Update-PodeWebCheckbox). When using the latter you will need to supply the options as Option elements, for this you can either build the options using [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption) or convert raw values via [`ConvertTo-PodeWebOption`](../../../Functions/Elements/ConvertTo-PodeWebOption). + +The following will both build a Checkbox element with 10 random numbers as the options, using the above methods: + +```powershell +New-PodeWebCard -Content @( + New-PodeWebForm -Name 'Example' -ScriptBlock { + # return raw values for Pode.Web to convert + New-PodeWebCheckbox -Name 'RawValues' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + # use New-PodeWebOption + New-PodeWebCheckbox -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + New-PodeWebOption -Name (Get-Random -Minimum 1 -Maximum 10) + }) + + $options | Update-PodeWebCheckbox -Id $ElementData.Id + } + + # use ConvertTo-PodeWebOption + New-PodeWebCheckbox -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + }) + + $options | + ConvertTo-PodeWebOption | + Update-PodeWebCheckbox -Id $ElementData.Id + } + } +) +``` + ## Display Name By default the label displays the `-Name` of the element. You can change the value displayed by also supplying an optional `-DisplayName` value; this value is purely visual, when the user submits the form the value of the element is still retrieved using the `-Name` from `$WebEvent.Data`. -## Display Options - -By default the options displayed are from the `-Options` parameter. Like the Name, you can change the values displayed by supplying the optional `-DisplayOptions` - values in the array should be in the same order as the values in `-Options`. These values are, like the Display Name, purely visual, and when the form is submitted the server receives the original values from `-Options`. +The same principle applies to `New-PodeWebOption` and `-DisplayName`. diff --git a/docs/Tutorials/Elements/Form.md b/docs/Tutorials/Elements/Form.md index 0cd0da0a..9a8a198b 100644 --- a/docs/Tutorials/Elements/Form.md +++ b/docs/Tutorials/Elements/Form.md @@ -19,10 +19,14 @@ New-PodeWebCard -Content @( New-PodeWebTextbox -Name 'Time' -Type Time New-PodeWebDateTime -Name 'DateTime' New-PodeWebCredential -Name 'Credentials' - New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch - New-PodeWebRadio -Name 'Radios' -Options @('S', 'M', 'L') + New-PodeWebCheckbox -Name 'Checkboxes' -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) -AsSwitch + New-PodeWebRadio -Name 'Radios' -Options @( + 'S', 'M', 'L' | ConvertTo-PodeWebOption + ) New-PodeWebSelect -Name 'Role' -Multiple -Options @( - @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + 'User', 'Admin', 'Operations' | ConvertTo-PodeWebOption ) New-PodeWebRange -Name 'Cores' -Value 30 -ShowValue ) diff --git a/docs/Tutorials/Elements/Modal.md b/docs/Tutorials/Elements/Modal.md index 742cd5ad..792c039d 100644 --- a/docs/Tutorials/Elements/Modal.md +++ b/docs/Tutorials/Elements/Modal.md @@ -70,7 +70,7 @@ New-PodeWebCard -Content @( $editBtn = New-PodeWebButton -Name 'Edit' -Icon 'Edit' -IconOnly -ScriptBlock { $svc = Get-Service -Name $WebEvent.Data.Value Show-PodeWebModal -Name 'Edit Service' -DataValue $WebEvent.Data.Value -Actions @( - Set-PodeWebSelect -Name 'StartType' -Value $svc.StartType + Select-PodeWebSelectOption -Name 'StartType' -OptionName $svc.StartType ) } diff --git a/docs/Tutorials/Elements/Radio.md b/docs/Tutorials/Elements/Radio.md index a18bbd35..cd798998 100644 --- a/docs/Tutorials/Elements/Radio.md +++ b/docs/Tutorials/Elements/Radio.md @@ -1,17 +1,28 @@ # Radio -| Support | | -| ------- |-| -| Events | Yes | +| Support | | +| ------- | --- | +| Events | Yes | The Radio element is a form input element, and can be added using [`New-PodeWebRadio`](../../../Functions/Elements/New-PodeWebRadio). This will add a series of radio buttons to your form: +## Options + +### Multiple + +To create a Radio element with multiple pre-defined options, you can use the `-Options` parameter which accepts an array of [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption): + ```powershell New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock { $bestLang = $WebEvent.Data['Best Language?'] } -Content @( - New-PodeWebRadio -Name 'Best Language?' -Options 'PowerShell', 'C#', 'Python', 'Other' + New-PodeWebRadio -Name 'Best Language?' -Options @( + New-PodeWebOption -Name 'PowerShell' - Selected + New-PodeWebOption -Name 'C#' + New-PodeWebOption -Name 'Python' + New-PodeWebOption -Name 'Other' + ) ) ) ``` @@ -20,10 +31,49 @@ Which looks like below: ![radio](../../../images/radio.png) +### Dynamic + +You can build a Radio element's options dynamically by using the `-ScriptBlock` parameter. This will allow you to retrieve the options from elsewhere for use with the Radio element. + +You can either return an array of raw string values, or pipe the options into, and return, [`Update-PodeWebRadio`](../../../Functions/Actions/Update-PodeWebRadio). When using the latter you will need to supply the options as Option elements, for this you can either build the options using [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption) or convert raw values via [`ConvertTo-PodeWebOption`](../../../Functions/Elements/ConvertTo-PodeWebOption). + +The following will both build a RAdio element with 10 random numbers as the options, using the above methods: + +```powershell +New-PodeWebCard -Content @( + New-PodeWebForm -Name 'Example' -ScriptBlock { + # return raw values for Pode.Web to convert + New-PodeWebRadio -Name 'RawValues' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + # use New-PodeWebOption + New-PodeWebRadio -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + New-PodeWebOption -Name (Get-Random -Minimum 1 -Maximum 10) + }) + + $options | Update-PodeWebRadio -Id $ElementData.Id + } + + # use ConvertTo-PodeWebOption + New-PodeWebRadio -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + }) + + $options | + ConvertTo-PodeWebOption | + Update-PodeWebRadio -Id $ElementData.Id + } + } +) +``` + ## Display Name By default the label displays the `-Name` of the element. You can change the value displayed by also supplying an optional `-DisplayName` value; this value is purely visual, when the user submits the form the value of the element is still retrieved using the `-Name` from `$WebEvent.Data`. -## Display Options - -By default the options displayed are from the `-Options` parameter. Like the Name, you can change the values displayed by supplying the optional `-DisplayOptions` - values in the array should be in the same order as the values in `-Options`. These values are, like the Display Name, purely visual, and when the form is submitted the server receives the original values from `-Options`. +The same principle applies to `New-PodeWebOption` and `-DisplayName`. diff --git a/docs/Tutorials/Elements/Select.md b/docs/Tutorials/Elements/Select.md index efa4829f..f317d42e 100644 --- a/docs/Tutorials/Elements/Select.md +++ b/docs/Tutorials/Elements/Select.md @@ -46,7 +46,7 @@ Which looks like below: You can build a Select element's options dynamically by using the `-ScriptBlock` parameter. This will allow you to retrieve the options from elsewhere for use with the Select element. -You can either return an array of raw values, or pipe the options into, and return, [`Update-PodeWebSelect`](../../../Functions/Actions/Update-PodeWebSelect). When using the latter you will need to supply the options as Option elements, for this you can either build the options using [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption) or convert raw values via [`ConvertTo-PodeWebOption`](../../../Functions/Elements/ConvertTo-PodeWebOption). +You can either return an array of raw string values, or pipe the options into, and return, [`Update-PodeWebSelect`](../../../Functions/Actions/Update-PodeWebSelect). When using the latter you will need to supply the options as Option elements, for this you can either build the options using [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption) or convert raw values via [`ConvertTo-PodeWebOption`](../../../Functions/Elements/ConvertTo-PodeWebOption). The following will both build a Select element with 10 random numbers as the options, using the above methods: @@ -90,3 +90,5 @@ You can render a multiple select element, where more than one option can be sele ## Display Name By default the label displays the `-Name` of the element. You can change the value displayed by also supplying an optional `-DisplayName` value; this value is purely visual, when the user submits the form the value of the element is still retrieved using the `-Name` from `$WebEvent.Data`. + +The same principle applies to `New-PodeWebOption` and `-DisplayName`. \ No newline at end of file diff --git a/examples/checkboxes.ps1 b/examples/checkboxes.ps1 new file mode 100644 index 00000000..6c0357c4 --- /dev/null +++ b/examples/checkboxes.ps1 @@ -0,0 +1,164 @@ +Import-Module Pode -MaximumVersion 2.99.99 -Force +Import-Module ..\src\Pode.Web.psd1 -Force + +Start-PodeServer -Threads 2 { + # add a simple endpoint + Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Http + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # set the use of templates, and set a login page + Initialize-PodeWebTemplates -Title 'Checkboxes' -Theme Dark + + # form with a single checkbox option + $single = New-PodeWebForm -Name 'Single' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'SingleOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebCheckbox -Name 'SingleSwitch' -AsSwitch + + # toggle checkbox checked state + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'Select' -ScriptBlock { + Select-PodeWebCheckbox -Name 'SingleSwitch' + } + New-PodeWebButton -Name 'Deselect' -ScriptBlock { + Reset-PodeWebCheckbox -Name 'SingleSwitch' + } + ) + + # toggle checkbox enabled state + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'Enable' -ScriptBlock { + Enable-PodeWebCheckbox -Name 'SingleSwitch' + } + New-PodeWebButton -Name 'Disable' -ScriptBlock { + Disable-PodeWebCheckbox -Name 'SingleSwitch' + } + ) + ) + + # form with multiple checkbox options + $multi = New-PodeWebForm -Name 'Multi' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'MultiOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebCheckbox -Name 'MultiSwitch' -AsSwitch -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) + + # toggle checkbox checked state - all + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'SelectAll' -ScriptBlock { + Select-PodeWebCheckbox -Name 'MultiSwitch' + } + New-PodeWebButton -Name 'DeselectAll' -ScriptBlock { + Reset-PodeWebCheckbox -Name 'MultiSwitch' + } + ) + + # toggle checkbox checked state - terms only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'SelectTerms' -ScriptBlock { + Select-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Terms' + } + New-PodeWebButton -Name 'DeselectTerms' -ScriptBlock { + Reset-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Terms' + } + ) + + # toggle checkbox checked state - privacy only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'SelectPrivacy' -ScriptBlock { + Select-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Privacy' + } + New-PodeWebButton -Name 'DeselectPrivacy' -ScriptBlock { + Reset-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Privacy' + } + ) + + # toggle checkbox enabled state - all + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'EnableAll' -ScriptBlock { + Enable-PodeWebCheckbox -Name 'MultiSwitch' + } + New-PodeWebButton -Name 'DisableAll' -ScriptBlock { + Disable-PodeWebCheckbox -Name 'MultiSwitch' + } + ) + + # toggle checkbox enabled state - terms only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'EnableTerms' -ScriptBlock { + Enable-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Terms' + } + New-PodeWebButton -Name 'DisableTerms' -ScriptBlock { + Disable-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Terms' + } + ) + + # toggle checkbox enabled state - privacy only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'EnablePrivacy' -ScriptBlock { + Enable-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Privacy' + } + New-PodeWebButton -Name 'DisablePrivacy' -ScriptBlock { + Disable-PodeWebCheckbox -Name 'MultiSwitch' -OptionName 'Privacy' + } + ) + ) + + # form with random checkbox options + $random = New-PodeWebForm -Name 'Random' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'RandomOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebCheckbox -Name 'RandomSwitch' -AsSwitch -Options @( + 1..5 | ForEach-Object { Get-Random -Minimum 1 -Maximum 10 } | ConvertTo-PodeWebOption + ) + + New-PodeWebButtonGroup -Buttons @( + # clear options + New-PodeWebButton -Name 'Clear' -ScriptBlock { + Clear-PodeWebCheckbox -Name 'RandomSwitch' + } + + # add new options + New-PodeWebButton -Name 'Add' -ScriptBlock { + Add-PodeWebCheckboxOption -Name 'RandomSwitch' -Option @( + 1..5 | ForEach-Object { Get-Random -Minimum 1 -Maximum 10 } | ConvertTo-PodeWebOption + ) + } + + # remove options + New-PodeWebButton -Name 'Remove' -ScriptBlock { + Remove-PodeWebCheckboxOption -Name 'RandomSwitch' -OptionName (Get-Random -Minimum 1 -Maximum 10) + } + ) + ) + + # form with dynamic checkbox options + $dynamic = New-PodeWebForm -Name 'Dynamic' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'DynamicOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebCheckbox -Name 'DynamicSwitch' -AsSwitch -ScriptBlock { + foreach ($i in (1..5)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButtonGroup -Buttons @( + # sync new options + New-PodeWebButton -Name 'Sync' -ScriptBlock { + Sync-PodeWebCheckbox -Name 'DynamicSwitch' + } + ) + ) + + # add forms to page + Add-PodeWebPage -Name 'Home' -Path '/' -Content $single, $multi, $random, $dynamic -Title 'Testing Checkboxes' -HomePage +} \ No newline at end of file diff --git a/examples/input_events.ps1 b/examples/input_events.ps1 index 1e1d4026..9dec5687 100644 --- a/examples/input_events.ps1 +++ b/examples/input_events.ps1 @@ -70,7 +70,9 @@ Start-PodeServer -Threads 2 { # radio event $radio = New-PodeWebContainer -Content @( New-PodeWebText -Value 'Select options: ' - New-PodeWebRadio -Name 'Options' -Options 'Bellow 1', 'Bellow 2', 'Bellow 3' | + New-PodeWebRadio -Name 'Options' -Options @( + 'Bellow 1', 'Bellow 2', 'Bellow 3' | ConvertTo-PodeWebOption + ) | Register-PodeWebEvent -Type Change -ScriptBlock { Open-PodeWebBellow -Name $WebEvent.Data['Options'] } @@ -94,7 +96,9 @@ Start-PodeServer -Threads 2 { # checkbox event $checkbox = New-PodeWebContainer -Content @( New-PodeWebText -Value 'Select options: ' - New-PodeWebCheckbox -Name 'Options' -Options 'Bellow 1', 'Bellow 2', 'Bellow 3' | + New-PodeWebCheckbox -Name 'Options' -Options @( + 'Bellow 1', 'Bellow 2', 'Bellow 3' | ConvertTo-PodeWebOption + ) | Register-PodeWebEvent -Type Change -ScriptBlock { if (!$WebEvent.Data['Options']) { Close-PodeWebAccordion -Name 'Accordion3' diff --git a/examples/inputs.ps1 b/examples/inputs.ps1 index b0d63e03..6ede834b 100644 --- a/examples/inputs.ps1 +++ b/examples/inputs.ps1 @@ -37,9 +37,21 @@ Start-PodeServer -Threads 2 { New-PodeWebDateTime -Name 'DateTime' -DateValue '2023-12-23' -TimeValue '13:37' New-PodeWebCredential -Name 'Credentials' New-PodeWebMinMax -Name 'CPU' -AppendIcon 'percent' -ReadOnly - New-PodeWebCheckbox -Name 'Switches' -Options @('Terms', 'Privacy') -AsSwitch - New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -Inline -HelpText 'Accept the terms and privacy policy' - New-PodeWebRadio -Name 'Radios' -Options @('S', 'M', 'L') -HelpText 'Select a size' + + New-PodeWebCheckbox -Name 'Do you agree?' -AsSwitch + + New-PodeWebCheckbox -Name 'Switches' -AsSwitch -Options @( + 'Terms', 'Privacy' | ConvertTo-PodeWebOption + ) + + New-PodeWebCheckbox -Name 'Checkboxes' -Inline -HelpText 'Accept the terms and privacy policy' -Disabled -Options @( + New-PodeWebOption -Name 'Terms' + New-PodeWebOption -Name 'Privacy' -Selected + ) + + New-PodeWebRadio -Name 'Radios' -HelpText 'Select a size' -Options @( + 'S', 'M', 'L' | ConvertTo-PodeWebOption + ) New-PodeWebSelect -Name 'Role1' -PrependIcon 'account' -AppendIcon 'account' -HelpText 'Select a role' -Options @( @('Choose...', 'User', 'Admin', 'Operations') | ConvertTo-PodeWebOption diff --git a/examples/radios.ps1 b/examples/radios.ps1 new file mode 100644 index 00000000..a62cacd8 --- /dev/null +++ b/examples/radios.ps1 @@ -0,0 +1,98 @@ +Import-Module Pode -MaximumVersion 2.99.99 -Force +Import-Module ..\src\Pode.Web.psd1 -Force + +Start-PodeServer -Threads 2 { + # add a simple endpoint + Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Http + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # set the use of templates, and set a login page + Initialize-PodeWebTemplates -Title 'Radios' -Theme Dark + + # form with multiple radio options + $multi = New-PodeWebForm -Name 'Multi' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'MultiOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebRadio -Name 'MultipleRadio' -Inline -Options @( + 'S', 'M', 'L' | ConvertTo-PodeWebOption + ) + + # toggle radio checked state + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'S' -ScriptBlock { + Select-PodeWebRadio -Name 'MultipleRadio' -OptionName 'S' + } + New-PodeWebButton -Name 'M' -ScriptBlock { + Select-PodeWebRadio -Name 'MultipleRadio' -OptionName 'M' + } + New-PodeWebButton -Name 'L' -ScriptBlock { + Select-PodeWebRadio -Name 'MultipleRadio' -OptionName 'L' + } + ) + + # toggle radio enabled state - all + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'EnableAll' -ScriptBlock { + Enable-PodeWebRadio -Name 'MultipleRadio' + } + New-PodeWebButton -Name 'DisableAll' -ScriptBlock { + Disable-PodeWebRadio -Name 'MultipleRadio' + } + ) + + # toggle radio enabled state - S only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'Enable-S' -ScriptBlock { + Enable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'S' + } + New-PodeWebButton -Name 'Disable-S' -ScriptBlock { + Disable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'S' + } + ) + + # toggle radio enabled state - M only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'Enable-M' -ScriptBlock { + Enable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'M' + } + New-PodeWebButton -Name 'Disable-M' -ScriptBlock { + Disable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'M' + } + ) + + # toggle radio enabled state - L only + New-PodeWebButtonGroup -Buttons @( + New-PodeWebButton -Name 'Enable-L' -ScriptBlock { + Enable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'L' + } + New-PodeWebButton -Name 'Disable-L' -ScriptBlock { + Disable-PodeWebRadio -Name 'MultipleRadio' -OptionName 'L' + } + ) + ) + + # form with dynamic radio options + $dynamic = New-PodeWebForm -Name 'Dynamic' -ButtonType Submit -AsCard -ScriptBlock { + $WebEvent.Data | + New-PodeWebTextbox -Name 'DynamicOutput' -Multiline -Size 5 -Preformat -AsJson | + Out-PodeWebElement + } -Content @( + New-PodeWebRadio -Name 'DynamicRadio' -ScriptBlock { + foreach ($i in (1..5)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButtonGroup -Buttons @( + # sync new options + New-PodeWebButton -Name 'Sync' -ScriptBlock { + Sync-PodeWebRadio -Name 'DynamicRadio' + } + ) + ) + + # add forms to page + Add-PodeWebPage -Name 'Home' -Path '/' -Content $multi, $dynamic -Title 'Testing Radios' -HomePage +} \ No newline at end of file diff --git a/src/Private/Helpers.ps1 b/src/Private/Helpers.ps1 index 0cd69781..a7f2ca22 100644 --- a/src/Private/Helpers.ps1 +++ b/src/Private/Helpers.ps1 @@ -322,10 +322,11 @@ function Test-PodeWebColour { function Test-PodeWebArrayEmpty { param( [Parameter()] + [array] $Array ) - return (($null -eq $Array) -or (@($Array).Length -eq 0)) + return (($null -eq $Array) -or ($Array.Length -eq 0)) } function Test-PodeWebPageAccess { diff --git a/src/Public/Actions.ps1 b/src/Public/Actions.ps1 index 8d38748a..f6f5554c 100644 --- a/src/Public/Actions.ps1 +++ b/src/Public/Actions.ps1 @@ -780,7 +780,7 @@ function Update-PodeWebText { } } -function Set-PodeWebSelect { +function Select-PodeWebSelectOption { [CmdletBinding(DefaultParameterSetName = 'Name')] param( [Parameter(Mandatory = $true, ParameterSetName = 'Name')] @@ -797,7 +797,7 @@ function Set-PodeWebSelect { ) Send-PodeWebAction -Value @{ - Operation = 'Set' + Operation = 'Select' ObjectType = 'Select' Name = $Name ID = $Id @@ -830,7 +830,9 @@ function Add-PodeWebSelectOption { } process { - $items += $Option + if ($null -ne $Option) { + $items += $Option + } } end { @@ -877,13 +879,25 @@ function Remove-PodeWebSelectOption { $GroupName ) - Send-PodeWebAction -Value @{ - Operation = 'Remove' - ObjectType = 'Select' - Name = $Name - ID = $Id - OptionName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'OptionName' -Value $OptionName) - GroupName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'GroupName' -Value $GroupName) + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Remove' + ObjectType = 'Select' + Name = $Name + ID = $Id + OptionName = $items + GroupName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'GroupName' -Value $GroupName) + } } } @@ -919,7 +933,9 @@ function Update-PodeWebSelect { } process { - $items += $Options + if ($null -ne $Options) { + $items += $Options + } } end { @@ -980,7 +996,7 @@ function Sync-PodeWebSelect { } } -function Set-PodeWebDatalist { +function Select-PodeWebDatalistOption { [CmdletBinding(DefaultParameterSetName = 'Name')] param( [Parameter(Mandatory = $true, ParameterSetName = 'Name')] @@ -997,7 +1013,7 @@ function Set-PodeWebDatalist { ) Send-PodeWebAction -Value @{ - Operation = 'Set' + Operation = 'Select' ObjectType = 'Datalist' Name = $Name ID = $Id @@ -1029,7 +1045,9 @@ function Add-PodeWebDatalistOption { } process { - $items += $Option + if ($null -ne $Option) { + $items += $Option + } } end { @@ -1060,7 +1078,7 @@ function Remove-PodeWebDatalistOption { [string] $Id, - [Parameter(ValueFromPipeline = $true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string[]] $OptionName, @@ -1068,13 +1086,25 @@ function Remove-PodeWebDatalistOption { $NoAutoSelect ) - Send-PodeWebAction -Value @{ - Operation = 'Remove' - ObjectType = 'Datalist' - Name = $Name - ID = $Id - OptionName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'OptionName' -Value $OptionName) - NoAutoSelect = $NoAutoSelect.IsPresent + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Remove' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + OptionName = $items + NoAutoSelect = $NoAutoSelect.IsPresent + } } } @@ -1108,7 +1138,9 @@ function Update-PodeWebDatalist { } process { - $items += $Options + if ($null -ne $Options) { + $items += $Options + } } end { @@ -1206,25 +1238,46 @@ function Update-PodeWebCheckbox { [string] $Name, - [Parameter()] - [int] - $OptionId = 0, + [Parameter(ValueFromPipeline = $true)] + [hashtable[]] + $Options, [switch] $Disabled, [switch] - $Checked + $Required, + + [Alias('Checked')] + [switch] + $Selected ) - Send-PodeWebAction -Value @{ - Operation = 'Update' - ObjectType = 'Checkbox' - ID = $Id - Name = $Name - OptionId = $OptionId - Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) - Checked = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Checked' -Value $Checked.IsPresent) + begin { + $items = @() + } + + process { + if ($null -ne $Options) { + $items += $Options + } + } + + end { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Checkbox can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Update' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + Options = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Options' -Value $items) + Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) + Required = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Required' -Value $Required.IsPresent) + Selected = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Selected' -Value $Selected.IsPresent) + } } } @@ -1239,17 +1292,29 @@ function Enable-PodeWebCheckbox { [string] $Name, - [Parameter()] - [int] - $OptionId = 0 + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName ) - Send-PodeWebAction -Value @{ - Operation = 'Enable' - ObjectType = 'Checkbox' - ID = $Id - Name = $Name - OptionId = $OptionId + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Enable' + ObjectType = 'Checkbox' + ID = $Id + Name = $Name + OptionName = $items + } } } @@ -1264,17 +1329,514 @@ function Disable-PodeWebCheckbox { [string] $Name, - [Parameter()] - [int] - $OptionId = 0 + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName ) - Send-PodeWebAction -Value @{ - Operation = 'Disable' - ObjectType = 'Checkbox' - ID = $Id - Name = $Name - OptionId = $OptionId + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Disable' + ObjectType = 'Checkbox' + ID = $Id + Name = $Name + OptionName = $items + } + } +} + +function Select-PodeWebCheckbox { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Select' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + OptionName = $items + } + } +} + +function Reset-PodeWebCheckbox { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Reset' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + OptionName = $items + } + } +} + +function Add-PodeWebCheckboxOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [hashtable[]] + $Option + ) + + begin { + $items = @() + } + + process { + if ($null -ne $Option) { + $items += $Option + } + } + + end { + # ensure options are only of type option + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Checkbox can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Add' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + Options = $items + } + } +} + +function Remove-PodeWebCheckboxOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Remove' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + OptionName = $items + } + } +} + +function Clear-PodeWebCheckbox { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Clear' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + } +} + +function Sync-PodeWebCheckbox { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Sync' + ObjectType = 'Checkbox' + Name = $Name + ID = $Id + } +} + +function Update-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Id')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(ValueFromPipeline = $true)] + [hashtable[]] + $Options, + + [switch] + $Disabled, + + [switch] + $Required + ) + + begin { + $items = @() + } + + process { + if ($null -ne $Options) { + $items += $Options + } + } + + end { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Radio can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Update' + ObjectType = 'Radio' + Name = $Name + ID = $Id + Options = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Options' -Value $items) + Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) + Required = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Required' -Value $Required.IsPresent) + } + } +} + +function Enable-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Id')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Enable' + ObjectType = 'Radio' + ID = $Id + Name = $Name + OptionName = $items + } + } +} + +function Disable-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Id')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Disable' + ObjectType = 'Radio' + ID = $Id + Name = $Name + OptionName = $items + } + } +} + +function Select-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string] + $OptionName + ) + + Send-PodeWebAction -Value @{ + Operation = 'Select' + ObjectType = 'Radio' + Name = $Name + ID = $Id + OptionName = $OptionName + } +} + +function Reset-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string] + $OptionName + ) + + Send-PodeWebAction -Value @{ + Operation = 'Reset' + ObjectType = 'Radio' + Name = $Name + ID = $Id + OptionName = $OptionName + } +} + +function Add-PodeWebRadioOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [hashtable[]] + $Option + ) + + begin { + $items = @() + } + + process { + if ($null -ne $Option) { + $items += $Option + } + } + + end { + # ensure options are only of type option + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Radio can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Add' + ObjectType = 'Radio' + Name = $Name + ID = $Id + Options = $items + } + } +} + +function Remove-PodeWebRadioOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string[]] + $OptionName + ) + + begin { + $items = @() + } + + process { + if (![string]::IsNullOrEmpty($OptionName)) { + $items += $OptionName + } + } + + end { + Send-PodeWebAction -Value @{ + Operation = 'Remove' + ObjectType = 'Radio' + Name = $Name + ID = $Id + OptionName = $items + } + } +} + +function Clear-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Clear' + ObjectType = 'Radio' + Name = $Name + ID = $Id + } +} + +function Sync-PodeWebRadio { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Sync' + ObjectType = 'Radio' + Name = $Name + ID = $Id } } diff --git a/src/Public/Elements.ps1 b/src/Public/Elements.ps1 index 993320bd..fc306255 100644 --- a/src/Public/Elements.ps1 +++ b/src/Public/Elements.ps1 @@ -357,7 +357,7 @@ function New-PodeWebCode { } function New-PodeWebCheckbox { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Single')] param( [Parameter(Mandatory = $true)] [string] @@ -372,26 +372,38 @@ function New-PodeWebCheckbox { $Id, [Parameter(ParameterSetName = 'Multiple')] - [string[]] + [hashtable[]] $Options, - [Parameter(ParameterSetName = 'Multiple')] - [string[]] - $DisplayOptions, + [Parameter(ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, [Parameter()] [string] $HelpText, + [Parameter(ParameterSetName = 'ScriptBlock')] + [Alias('NoAuth')] + [switch] + $NoAuthentication, + [Parameter(ParameterSetName = 'Multiple')] + [Parameter(ParameterSetName = 'ScriptBlock')] [switch] $Inline, [switch] $AsSwitch, + [Parameter(ParameterSetName = 'Single')] + [Alias('Checked')] [switch] - $Checked, + $Selected, [switch] $Disabled, @@ -400,36 +412,121 @@ function New-PodeWebCheckbox { $Required, [switch] - $HideName + $HideName, + + [Parameter(ParameterSetName = 'Empty')] + [switch] + $AllowEmpty ) + # options can only be of type Option + if (!(Test-PodeWebContent -Content $Options -ComponentType Element -ObjectType Option)) { + throw 'A Checkbox can only contain Options' + } + + # generate an ID $Id = Get-PodeWebElementId -Tag Checkbox -Id $Id -Name $Name - if (($null -eq $Options) -or ($Options.Length -eq 0)) { - $Options = @('true') + # handle single checkbox with no options, unless -AllowEmpty is specified + if (!$AllowEmpty -and (($null -eq $Options) -or ($Options.Length -eq 0)) -and ($null -eq $ScriptBlock)) { + $opt = New-PodeWebOption -Name 'pode_web_true' -Selected:$Selected + $opt.Required = $Required.IsPresent + $Options = @($opt) } - return @{ - Operation = 'New' - ComponentType = 'Element' - ObjectType = 'Checkbox' - Name = $Name - DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) - HideName = $HideName.IsPresent - ID = $Id - Options = @($Options) - DisplayOptions = @(Protect-PodeWebValues -Value $DisplayOptions -Default $Options -EqualCount -Encode) - HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) - Inline = $Inline.IsPresent - AsSwitch = $AsSwitch.IsPresent - Checked = $Checked.IsPresent - Disabled = $Disabled.IsPresent - Required = $Required.IsPresent + # build element + $element = @{ + Operation = 'New' + ComponentType = 'Element' + ObjectType = 'Checkbox' + Name = $Name + DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) + HideName = $HideName.IsPresent + ID = $Id + Options = $Options + IsDynamic = ($null -ne $ScriptBlock) + HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) + Inline = $Inline.IsPresent + AsSwitch = $AsSwitch.IsPresent + NoAuthentication = $NoAuthentication.IsPresent + Disabled = $Disabled.IsPresent + Required = $Required.IsPresent + } + + # create dynamic options route + $routePath = "/pode.web-dynamic/elements/checkbox/$($Id)/options" + if (($null -ne $ScriptBlock) -and !(Test-PodeWebRoute -Path $routePath)) { + # check for scoped vars + $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + $elementLogic = @{ + ScriptBlock = $ScriptBlock + UsingVariables = $usingVars + } + + $auth = $null + if (!$NoAuthentication -and !$PageData.NoAuthentication) { + $auth = (Get-PodeWebState -Name 'auth') + } + + if (Test-PodeIsEmpty $EndpointName) { + $EndpointName = Get-PodeWebState -Name 'endpoint-name' + } + + $argList = @( + @{ Data = $ArgumentList }, + $element, + $ElementData, + $elementLogic + ) + + Add-PodeRoute -Method Post -Path $routePath -Authentication $auth -ArgumentList $argList -EndpointName $EndpointName -ScriptBlock { + param($Data, $Element, $Parent, $Logic) + $global:ElementData = $Element + $global:ParentData = $Parent + Set-PodeWebMetadata + + $result = @(Invoke-PodeWebScriptBlock -Logic $Logic -Arguments $Data.Data) + + $wrapped = $null + if (Test-PodeWebActionsAsync) { + if ($result.Length -gt 0) { + if ($null -eq $result[0]) { + $result = @() + } + + $wrapped, $result = Split-PodeWebDynamicOutput -Output $result + } + } + else { + if ($null -eq $result) { + $result = @() + } + + $wrapped, $result = Split-PodeWebDynamicOutput -Output $result + } + + if ($result.Length -gt 0) { + $result = $result | + ConvertTo-PodeWebOption | + Update-PodeWebCheckbox -Id $ElementData.ID + } + + $result = Join-PodeWebDynamicOutput -Wrapped $wrapped -Output $result + + if (($null -ne $result) -and ($result.Length -gt 0)) { + Write-PodeJsonResponse -Value $result + } + + $global:ElementData = $null + $global:ParentData = $null + } } + + return $element } function New-PodeWebRadio { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Options')] param( [Parameter(Mandatory = $true)] [string] @@ -443,18 +540,28 @@ function New-PodeWebRadio { [string] $Id, - [Parameter(Mandatory = $true)] - [string[]] + [Parameter(Mandatory = $true, ParameterSetName = 'Options')] + [ValidateNotNullOrEmpty()] + [hashtable[]] $Options, - [Parameter()] - [string[]] - $DisplayOptions, + [Parameter(ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, [Parameter()] [string] $HelpText, + [Parameter(ParameterSetName = 'ScriptBlock')] + [Alias('NoAuth')] + [switch] + $NoAuthentication, + [switch] $Inline, @@ -465,26 +572,122 @@ function New-PodeWebRadio { $Required, [switch] - $HideName + $HideName, + + [Parameter(ParameterSetName = 'Empty')] + [switch] + $AllowEmpty ) + # options can only be of type Option + if (!(Test-PodeWebContent -Content $Options -ComponentType Element -ObjectType Option)) { + throw 'A Radio can only contain Options' + } + + # error if multiple selected values + $selectedCount = 0 + + foreach ($option in $Options) { + if ($option.Selected) { + $selectedCount++ + } + } + + if ($selectedCount -ge 2) { + throw 'Radio cannot have multiple selected options' + } + + # generate an ID $Id = Get-PodeWebElementId -Tag Radio -Id $Id -Name $Name - return @{ - Operation = 'New' - ComponentType = 'Element' - ObjectType = 'Radio' - Name = $Name - DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) - HideName = $HideName.IsPresent - ID = $Id - Options = @($Options) - DisplayOptions = @(Protect-PodeWebValues -Value $DisplayOptions -Default $Options -EqualCount -Encode) - HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) - Inline = $Inline.IsPresent - Disabled = $Disabled.IsPresent - Required = $Required.IsPresent + # build element + $element = @{ + Operation = 'New' + ComponentType = 'Element' + ObjectType = 'Radio' + Name = $Name + DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) + HideName = $HideName.IsPresent + ID = $Id + Options = $Options + IsDynamic = ($null -ne $ScriptBlock) + HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) + Inline = $Inline.IsPresent + NoAuthentication = $NoAuthentication.IsPresent + Disabled = $Disabled.IsPresent + Required = $Required.IsPresent } + + # create dynamic options route + $routePath = "/pode.web-dynamic/elements/radio/$($Id)/options" + if (($null -ne $ScriptBlock) -and !(Test-PodeWebRoute -Path $routePath)) { + # check for scoped vars + $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + $elementLogic = @{ + ScriptBlock = $ScriptBlock + UsingVariables = $usingVars + } + + $auth = $null + if (!$NoAuthentication -and !$PageData.NoAuthentication) { + $auth = (Get-PodeWebState -Name 'auth') + } + + if (Test-PodeIsEmpty $EndpointName) { + $EndpointName = Get-PodeWebState -Name 'endpoint-name' + } + + $argList = @( + @{ Data = $ArgumentList }, + $element, + $ElementData, + $elementLogic + ) + + Add-PodeRoute -Method Post -Path $routePath -Authentication $auth -ArgumentList $argList -EndpointName $EndpointName -ScriptBlock { + param($Data, $Element, $Parent, $Logic) + $global:ElementData = $Element + $global:ParentData = $Parent + Set-PodeWebMetadata + + $result = @(Invoke-PodeWebScriptBlock -Logic $Logic -Arguments $Data.Data) + + $wrapped = $null + if (Test-PodeWebActionsAsync) { + if ($result.Length -gt 0) { + if ($null -eq $result[0]) { + $result = @() + } + + $wrapped, $result = Split-PodeWebDynamicOutput -Output $result + } + } + else { + if ($null -eq $result) { + $result = @() + } + + $wrapped, $result = Split-PodeWebDynamicOutput -Output $result + } + + if ($result.Length -gt 0) { + $result = $result | + ConvertTo-PodeWebOption | + Update-PodeWebRadio -Id $ElementData.ID + } + + $result = Join-PodeWebDynamicOutput -Wrapped $wrapped -Output $result + + if (($null -ne $result) -and ($result.Length -gt 0)) { + Write-PodeJsonResponse -Value $result + } + + $global:ElementData = $null + $global:ParentData = $null + } + } + + return $element } function New-PodeWebSelect { @@ -539,6 +742,11 @@ function New-PodeWebSelect { [string] $HelpText, + [Parameter(ParameterSetName = 'ScriptBlock')] + [Alias('NoAuth')] + [switch] + $NoAuthentication, + [switch] $Multiple, @@ -615,6 +823,7 @@ function New-PodeWebSelect { Disabled = $Disabled.IsPresent } + # create dynamic options route $routePath = "/pode.web-dynamic/elements/select/$($Id)/options" if (($null -ne $ScriptBlock) -and !(Test-PodeWebRoute -Path $routePath)) { # check for scoped vars @@ -925,10 +1134,6 @@ function New-PodeWebOption { [string] $DisplayName, - [Parameter()] - [string] - $Label, - [switch] $Disabled, @@ -936,13 +1141,13 @@ function New-PodeWebOption { $Selected ) + # build element return @{ Operation = 'New' ComponentType = 'Element' ObjectType = 'Option' Name = $Name DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) - Label = (Protect-PodeWebValue -Value $Label -Encode) Disabled = $Disabled.IsPresent Selected = $Selected.IsPresent } diff --git a/src/Templates/Public/scripts/templates.js b/src/Templates/Public/scripts/templates.js index 4099387f..a34c5680 100644 --- a/src/Templates/Public/scripts/templates.js +++ b/src/Templates/Public/scripts/templates.js @@ -594,6 +594,10 @@ class PodeElement { case 'step': this.steps(data, sender, opts); break; + + case 'select': + this.select(data, sender, opts); + break; } if (this.ephemeral) { @@ -1296,6 +1300,10 @@ class PodeElement { throw `${this.getType()} "steps" method not implemented`; } + select(data, sender, opts) { + throw `${this.getType()} "select" method not implemented`; + } + update(data, sender, opts) { if (data == null) { return; @@ -1701,14 +1709,12 @@ class PodeFormElement extends PodeContentElement { if (this.parent instanceof PodeForm) { var formGroup = !this.inForm ? 'd-inline-block' : `form-group row mb-3`; var divTag = this.asFieldset ? 'fieldset' : 'div'; - var idProps = this.asFieldset ? `id='${this.id}' pode-object='${this.getType()}' pode-id='${this.uuid}'` : ''; var events = this.asFieldset ? `pode-events-status='off'` : ''; var width = this.inForm || !this.width ? '' : `width:${this.width}`; html = `<${divTag} class='pode-form-${this.getType()} ${formGroup}' style='${width}' - ${idProps} ${events}> ${html} `; @@ -1776,17 +1782,22 @@ class PodeFormElement extends PodeContentElement { class PodeFormOptionElement extends PodeFormElement { constructor(data, sender, opts) { super(data, sender, opts); + this.optionCount = 0; } buildOptions(options) { var html = ''; + if (!options) { + return html; + } - convertToArray(options).forEach((opt) => { - if (opt.Options) { - html += this.buildGroup(opt); + convertToArray(options).forEach((option) => { + if (option.Options) { + html += this.buildGroup(option); } else { - html += this.buildOption(opt); + this.optionCount++; + html += this.buildOption(option); } }); @@ -1794,29 +1805,26 @@ class PodeFormOptionElement extends PodeFormElement { } buildOption(option) { - var label = option.Label ? ` label='${option.Label}'` : ''; - return ``; } + buildOptionId(option) { + return `${this.id}_option${this.optionCount}`; + } + buildGroup(group) { - var html = ``; - - convertToArray(group.Options).forEach((opt) => { - html += this.buildOption(opt); - }); - - html += ``; - return html; + ${group.Disabled ? 'disabled' : ''}> + ${this.buildOptions(group.Options)} + `; } } @@ -3794,7 +3802,7 @@ class PodeTextbox extends PodeFormElement { var autofocus = this.autofocus ? 'autofocus' : ''; var maxLength = data.MaxLength ? `maxlength='${data.MaxLength}'` : ''; - var width = `width:${this.width};`; + var width = this.width ? `width:${this.width};` : ''; var placeholder = data.Placeholder ? `placeholder='${encodeAttribute(data.Placeholder)}'` @@ -5395,7 +5403,7 @@ class PodeSelect extends PodeFormOptionElement { } } - set(data, sender, opts) { + select(data, sender, opts) { if (!data.OptionName) { return; } @@ -5522,7 +5530,7 @@ class PodeDatalist extends PodeFormOptionElement { } } - set(data, sender, opts) { + select(data, sender, opts) { if (!data.Value) { return; } @@ -5562,7 +5570,7 @@ class PodeDatalist extends PodeFormOptionElement { remove(data, sender, opts) { // remove any options - convertToArray(data.Value).forEach((opt) => { + convertToArray(data.OptionName).forEach((opt) => { this.getDatalist().find(`option[value='${opt}']`).remove(); }); @@ -5871,145 +5879,312 @@ class PodeProgress extends PodeContentElement { } PodeElementFactory.setClass(PodeProgress); -class PodeCheckbox extends PodeFormElement { +class PodeCheckbox extends PodeFormOptionElement { static type = 'checkbox'; - constructor(...args) { - super(...args); + constructor(data, sender, opts) { + super(data, sender, opts); this.asFieldset = true; + this.asSwitch = data.AsSwitch ?? false; + this.inline = data.Inline ?? false; + this.isSingle = !data.Options; } new(data, sender, opts) { - var inline = data.Inline ? 'form-check-inline' : '' - var checked = data.Checked ? 'checked' : ''; + var options = ''; + if (data.Options) { + options = this.buildOptions(data.Options); + } + + return `
+ ${options} +
`; + } - var isSwitch = data.AsSwitch ?? false; - var checkboxClass = isSwitch ? 'form-check form-switch' : 'form-check'; + buildOption(option) { + // classes and ID + var inline = this.inline ? 'form-check-inline' : ''; + var checkboxClass = this.asSwitch ? 'form-check form-switch' : 'form-check'; + var optId = this.buildOptionId(option); - data.Options = convertToArray(data.Options); - data.DisplayOptions = convertToArray(data.DisplayOptions); + // option name and label - setting "pode_web_true" as "true" and no label + var optName = option.Name; + var optLabel = option.DisplayName; - var options = ''; - data.Options.forEach((opt, index) => { - if (!opt) { - return; - } + if (optName == 'pode_web_true') { + optName = 'true'; + optLabel = ''; + } - options += `
- - -
`; - }); + // disabled/required - top level takes priority over option level + var disabled = this.disabled || (option.Disabled ?? false) ? 'disabled' : ''; + var required = this.required || (option.Required ?? false) ? 'required' : ''; - return `
${options}
`; + return `
+ + +
`; + } + + load(data, sender, opts) { + super.load(data, sender, opts); + if (this.dynamic) { + sendAjaxReq(`${this.url}/options`, null, this, true); + } } update(data, sender, opts) { super.update(data, sender, opts); - // get checkbox - var checkbox = this.getCheckbox(data.OptionId); - if (!checkbox) { - return; + // update options + if (data.Options != null) { + this.clear(); + this.element.append(this.buildOptions(data.Options)); } - // check or uncheck - if (data.Checked != null) { - checkbox.prop('checked', data.Checked); + // set checked/unchecked on all options? + if (data.Selected != null) { + if (data.Selected) { + this.select({}, sender, opts); + } + else { + this.reset({}, sender, opts); + } } } + setRequired(enabled) { + this.element.find(`input[type='checkbox']`).prop('required', enabled); + } + enable(data, sender, opts) { - var checkbox = this.getCheckbox(data.OptionId); - if (!checkbox) { - return; + // enable a specific option if OptionName supplied, else enable all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + enable(this.getCheckbox(opt)); + }); + } + else { + this.element.find(`input[type='checkbox']`).each((_, checkbox) => { + enable($(checkbox)); + }); } - - enable(checkbox); } disable(data, sender, opts) { - var checkbox = this.getCheckbox(data.OptionId); - if (!checkbox) { - return; + // disable a specific option if OptionName supplied, else disable all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + disable(this.getCheckbox(opt)); + }); + } + else { + this.element.find(`input[type='checkbox']`).each((_, checkbox) => { + disable($(checkbox)); + }); } + } + + select(data, sender, opts) { + // check a specific option if OptionName supplied, else check all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + this.getCheckbox(opt).prop('checked', true); + }); + } + else { + this.element.find(`input[type='checkbox']`).prop('checked', true); + } + + this.trigger('change'); + } + + reset(data, sender, opts) { + // uncheck a specific option if OptionName supplied, else uncheck all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + this.getCheckbox(opt).prop('checked', false); + }); + } + else { + this.element.find(`input[type='checkbox']`).prop('checked', false); + } + + this.trigger('change'); + } - disable(checkbox); + add(data, sender, opts) { + var html = this.buildOptions(data.Options); + this.element.append(html); } - getCheckbox(index) { - return this.element.find(`input#${this.id}_option${index}`); + remove(data, sender, opts) { + convertToArray(data.OptionName).forEach((opt) => { + this.getCheckbox(opt).closest('.form-check').remove(); + }); } - //TODO: same as the comment for "select" - CheckboxOption ? - // -- that would make this "Checkbox" not a "FormElement" but the "CheckOption" would be - // -- this is needed to control individual checkboxes, and fix disabled/required/etc support - // -- as well as update() support... + clear(data, sender, opts) { + this.element.empty(); + } + + getCheckbox(name) { + return this.element.find(`input[type='checkbox'][value='${name}']`); + } } PodeElementFactory.setClass(PodeCheckbox); -class PodeRadio extends PodeFormElement { +class PodeRadio extends PodeFormOptionElement { static type = 'radio'; - constructor(...args) { - super(...args); + constructor(data, sender, opts) { + super(data, sender, opts); this.label.asLegend = true; this.asFieldset = true; + this.inline = data.Inline ?? false; } new(data, sender, opts) { - var inline = data.Inline ? 'form-check-inline' : ''; + var options = ''; + if (data.Options) { + options = this.buildOptions(data.Options); + } - data.Options = convertToArray(data.Options); - data.DisplayOptions = convertToArray(data.DisplayOptions); + return `
+ ${options} +
`; + } - var options = ''; - data.Options.forEach((opt, index) => { - if (!opt) { - return; - } + buildOption(option) { + // classes and ID + var inline = this.inline ? 'form-check-inline' : ''; + var optId = this.buildOptionId(option); - options += `
- - -
`; - }); + // disabled/required - top level takes priority over option level + var disabled = this.disabled || (option.Disabled ?? false) ? 'disabled' : ''; + var required = this.required || (option.Required ?? false) ? 'required' : ''; + + return `
+ + +
`; + } + + load(data, sender, opts) { + super.load(data, sender, opts); + if (this.dynamic) { + sendAjaxReq(`${this.url}/options`, null, this, true); + } + } + + update(data, sender, opts) { + super.update(data, sender, opts); + + // update options + if (data.Options != null) { + this.clear(); + this.element.append(this.buildOptions(data.Options)); + } + } - return `
${options}
`; + setRequired(enabled) { + this.element.find(`input[type='radio']`).prop('required', enabled); + } + + enable(data, sender, opts) { + // enable a specific option if OptionName supplied, else enable all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + enable(this.getRadio(opt)); + }); + } + else { + this.element.find(`input[type='radio']`).each((_, radio) => { + enable($(radio)); + }); + } } - //TODO: same as the comment for "select" - CheckboxOption ? - // -- that would make this "Checkbox" not a "FormElement" but the "CheckOption" would be - // -- this is needed to control individual checkboxes, and fix disabled/required/etc support - // -- as well as update() support... + disable(data, sender, opts) { + // disable a specific option if OptionName supplied, else disable all options + if (data.OptionName && data.OptionName.length > 0) { + convertToArray(data.OptionName).forEach((opt) => { + disable(this.getRadio(opt)); + }); + } + else { + this.element.find(`input[type='radio']`).each((_, radio) => { + disable($(radio)); + }); + } + } + + select(data, sender, opts) { + // check a specific option + if (!data.OptionName) { + return; + } - //TODO: radio missing update/enable/etc. + this.getRadio(data.OptionName).prop('checked', true); + this.trigger('change'); + } + + reset(data, sender, opts) { + // uncheck a specific option + if (!data.OptionName) { + return; + } + + this.getRadio(data.OptionName).prop('checked', false); + this.trigger('change'); + } + + add(data, sender, opts) { + var html = this.buildOptions(data.Options); + this.element.append(html); + } + + remove(data, sender, opts) { + convertToArray(data.OptionName).forEach((opt) => { + this.getRadio(opt).closest('.form-check').remove(); + }); + } + + clear(data, sender, opts) { + this.element.empty(); + } + + getRadio(name) { + return this.element.find(`input[type='radio'][value='${name}']`); + } } PodeElementFactory.setClass(PodeRadio); diff --git a/src/Templates/Public/styles/default.css b/src/Templates/Public/styles/default.css index f8de2de8..95e7d2f1 100644 --- a/src/Templates/Public/styles/default.css +++ b/src/Templates/Public/styles/default.css @@ -508,22 +508,21 @@ span.pode-range-max { vertical-align: middle; } -input[type=date]::-webkit-calendar-picker-indicator:hover, -input[type=time]::-webkit-calendar-picker-indicator:hover, -input[type=datetime-local]::-webkit-calendar-picker-indicator:hover, -input[type=range]:hover, -div.form-switch label.form-check-label:hover, -div.form-check label.form-check-label:hover, -select.form-select:hover, -input[type=file]:hover, -input[type=checkbox]:hover, -input[type=radio]:hover { +input[type=date]:not(:disabled)::-webkit-calendar-picker-indicator:hover, +input[type=time]:not(:disabled)::-webkit-calendar-picker-indicator:hover, +input[type=datetime-local]:not(:disabled)::-webkit-calendar-picker-indicator:hover, +input[type=range]:not(:disabled):hover, +.form-check-input:not(:disabled)~label.form-check-label:hover, +select.form-select:not(:disabled):hover, +input[type=file]:not(:disabled):hover, +input[type=checkbox]:not(:disabled):hover, +input[type=radio]:not(:disabled):hover { cursor: pointer; } -input[type=date]:hover, -input[type=time]:hover, -input[type=datetime-local]:hover { +input[type=date]:not(:disabled):hover, +input[type=time]:not(:disabled):hover, +input[type=datetime-local]:not(:disabled):hover { cursor: text; }