diff --git a/docs/Getting-Started/Migrating/08-to-10.md b/docs/Getting-Started/Migrating/08-to-10.md index 23fb103a..87727224 100644 --- a/docs/Getting-Started/Migrating/08-to-10.md +++ b/docs/Getting-Started/Migrating/08-to-10.md @@ -153,6 +153,37 @@ Because of this change, functions that had `-Layouts` or `-Elements` parameters Originally, within the scriptblock of a dynamic element, if you wanted to access the data of an element's parent you could do so via `$ElementData.Parent`. This has now been changed, and the data is accessible via a new `$ParentData` instead - this has been done to stop the `$ElementData` object from getting too large, and to reduce network data transfer to better support SSE. +### Selects + +Select elements have been changed primarily in the way you supply Options. Originally you would create a Select via `New-PodeWebSelect` and supply `-Options` as a string array, and optionally `-DisplayOptions` or `-SelectedValue`. + +Now, you can create Options via the new `New-PodeWebOption` function - and even group them via `New-PodeWebOptionGroup`. The `-Options` on `New-PodeWebSelect` now accepts an array of these elements, and the `-DisplayOptions` and `-SelectedValue` have moved to `New-PodeWebOption` as `-DisplayName` and `-Selected` respectively. + +For example, the following previous Select element: + +```powershell +New-PodeWebSelect -Name 'Example' -Options @('Option1', 'Option2') -SelectedValue 'Option1' +``` + +Would now be the following instead: + +```powershell +New-PodeWebSelect -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, along with an optional `-SelectedOption` value, and it will convert to strings into Option elements for you: + +```powershell +New-PodeWebSelect -Name 'Example' -Options @( + @('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`. + ## Classes and Styles All element functions have had the `-CssClass` and `-CssStyle` parameters removed. Instead, you can now pipe the element into either `Add-PodeWebClass` or `Add-PodeWebStyle`: diff --git a/docs/Tutorials/Actions/Datalist.md b/docs/Tutorials/Actions/Datalist.md new file mode 100644 index 00000000..73794457 --- /dev/null +++ b/docs/Tutorials/Actions/Datalist.md @@ -0,0 +1,126 @@ +# Datalist + +This page details the actions available to Datalist elements. + +## Add + +To add one or more Options you can use [`Add-PodeWebDatalistOption`](../../../Functions/Actions/Add-PodeWebDatalistOption) along with `-Option`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Add Option' -ScriptBlock { + Add-PodeWebDatalistOption -Name 'Example' -Option @( + New-PodeWebOption -Name 'Added1' + ) + } +) +``` + +## Clear + +To clear the options of a Datalist element, you can use [`Clear-PodeWebDatalist`](../../../Functions/Actions/Clear-PodeWebDatalist): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Clear Datalist' -ScriptBlock { + Clear-PodeWebDatalist -Name 'Example' + } +) +``` + +## Remove + +To remove one or more Options you can use [`Remove-PodeWebDatalistOption`](../../../Functions/Actions/Remove-PodeWebDatalistOption) along with `-OptionName`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + New-PodeWebButton -Name 'Remove Options' -ScriptBlock { + Remove-PodeWebDatalistOption -Name 'Example' -OptionName 'Option1', 'Option3' + } +) +``` + +## Set + +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: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + + 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 + } + + New-PodeWebButton -Name 'Update Datalist Custom' -ScriptBlock { + Set-PodeWebDatalist -Name 'Example' -Value 'Custom Value' + } +) +``` + +## Sync + +If you built a Datalist element with the `-ScriptBlock` parameter, then you can re-invoke the scriptblock to update the element by using [`Sync-PodeWebDatalist`](../../../Functions/Actions/Sync-PodeWebDatalist): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebButton -Name 'Sync Datalist' -ScriptBlock { + Sync-PodeWebDatalist -Name 'Example' + } +) +``` + +## Update + +You can update a Datalist element's options by using [`Update-PodeWebDatalist`](../../../Functions/Actions/Update-PodeWebDatalist): + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebDatalist -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-PodeWebDatalist -Name 'Example' + } +) +``` + +Various other properties can be updated as well. diff --git a/docs/Tutorials/Actions/Error.md b/docs/Tutorials/Actions/Error.md index 4f145a74..246b5e2f 100644 --- a/docs/Tutorials/Actions/Error.md +++ b/docs/Tutorials/Actions/Error.md @@ -14,7 +14,9 @@ New-PodeWebCard -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch - New-PodeWebSelect -Name 'Role' -Options @('User', 'Admin', 'Operations') -Multiple + New-PodeWebSelect -Name 'Role' -Multiple -Options @( + @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) ) ) ``` diff --git a/docs/Tutorials/Actions/Form.md b/docs/Tutorials/Actions/Form.md index 8ce16219..b03c5902 100644 --- a/docs/Tutorials/Actions/Form.md +++ b/docs/Tutorials/Actions/Form.md @@ -12,7 +12,9 @@ New-PodeWebCard -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch - New-PodeWebSelect -Name 'Role' -Options @('User', 'Admin', 'Operations') -Multiple + New-PodeWebSelect -Name 'Role' -Multiple -Options @( + @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) ) ) @@ -33,7 +35,9 @@ New-PodeWebCard -Content @( New-PodeWebTextbox -Name 'Name' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch - New-PodeWebSelect -Name 'Role' -Options @('User', 'Admin', 'Operations') -Multiple + New-PodeWebSelect -Name 'Role' -Multiple -Options @( + @('User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) ) ) diff --git a/docs/Tutorials/Actions/Select.md b/docs/Tutorials/Actions/Select.md index 770b15b4..c7cdb04a 100644 --- a/docs/Tutorials/Actions/Select.md +++ b/docs/Tutorials/Actions/Select.md @@ -2,32 +2,101 @@ This page details the actions available to Select elements. +## Add + +To add one or more Options, or Option Groups, you can use [`Add-PodeWebSelectOption`](../../../Functions/Actions/Add-PodeWebSelectOption) along with `-Option` and optionally a `-GroupName` to place the options under: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebSelect -Name 'Example' -Options @( + New-PodeWebOptionGroup -Name 'Group1' -Options @( + New-PodeWebOption -Name 'Option1' + ) + New-PodeWebOption -Name 'Option2' + ) + + New-PodeWebButton -Name 'Add Option' -ScriptBlock { + Add-PodeWebSelectOption -Name 'Example' -Option @( + New-PodeWebOption -Name 'Added1' + ) + } + + New-PodeWebButton -Name 'Add Option to Group' -ScriptBlock { + Add-PodeWebSelectOption -Name 'Example' -GroupName 'Group1' -Option @( + New-PodeWebOption -Name 'Added1' + ) + } + + New-PodeWebButton -Name 'Add Options and Group' -ScriptBlock { + Add-PodeWebSelectOption -Name 'Example' -Option @( + New-PodeWebOptionGroup -Name 'AddedGroup1' -Options @( + New-PodeWebOption -Name 'Added1' + ) + ) + } +) +``` + ## Clear To clear the options of a Select element, you can use [`Clear-PodeWebSelect`](../../../Functions/Actions/Clear-PodeWebSelect): ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebSelect -Name Options -Options Option1, Option2, Option3 + New-PodeWebSelect -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) New-PodeWebButton -Name 'Clear Select' -ScriptBlock { - Clear-PodeWebSelect -Name Options + Clear-PodeWebSelect -Name 'Example' + } +) +``` + +## Remove + +To remove one or more Options, or Option Groups, you can use [`Remove-PodeWebSelectOption`](../../../Functions/Actions/Remove-PodeWebSelectOption) along with `-OptionName` and/or `-GroupName`: + +```powershell +New-PodeWebContainer -NoBackground -Content @( + New-PodeWebSelect -Name 'Example' -Options @( + New-PodeWebOptionGroup -Name 'Group1' -Options @( + New-PodeWebOption -Name 'Option1' + ) + New-PodeWebOptionGroup -Name 'Group2' -Options @( + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) + ) + + New-PodeWebButton -Name 'Remove Groups' -ScriptBlock { + Remove-PodeWebSelectOption -Name 'Example' -GroupName 'Group1', 'Group2' + } + + New-PodeWebButton -Name 'Remove Options' -ScriptBlock { + Remove-PodeWebSelectOption -Name 'Example' -OptionName 'Option1', 'Option3' } ) ``` ## Set -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 [`Set-PodeWebSelect`](../../../Functions/Actions/Set-PodeWebSelect): ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebSelect -Name Options -Options Option1, Option2, Option3 + New-PodeWebSelect -Name 'Example' -Options @( + New-PodeWebOption -Name 'Option1' + New-PodeWebOption -Name 'Option2' + New-PodeWebOption -Name 'Option3' + ) New-PodeWebButton -Name 'Update Select' -ScriptBlock { $rand = Get-Random -Minimum 0 -Maximum 3 $opt = (@('Option1', 'Option2', 'Option3'))[$rand] - Set-PodeWebSelect -Name Options -Value $opt + Set-PodeWebSelect -Name 'Example' -OptionName $opt } ) ``` @@ -38,14 +107,14 @@ If you built a Select element with the `-ScriptBlock` parameter, then you can re ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebSelect -Name Options --ScriptBlock { - return @(foreach ($i in (1..10)) { + New-PodeWebSelect -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { Get-Random -Minimum 1 -Maximum 10 - }) + } } New-PodeWebButton -Name 'Sync Select' -ScriptBlock { - Sync-PodeWebSelect -Name Options + Sync-PodeWebSelect -Name 'Example' } ) ``` @@ -56,10 +125,10 @@ You can update a Select element's options by using [`Update-PodeWebSelect`](../. ```powershell New-PodeWebContainer -NoBackground -Content @( - New-PodeWebSelect -Name Options --ScriptBlock { - return @(foreach ($i in (1..10)) { + New-PodeWebSelect -Name 'Example' -ScriptBlock { + foreach ($i in (1..10)) { Get-Random -Minimum 1 -Maximum 10 - }) + } } New-PodeWebButton -Name 'New Options' -ScriptBlock { @@ -67,11 +136,11 @@ New-PodeWebContainer -NoBackground -Content @( Get-Random -Minimum 1 -Maximum 10 }) - $options | Update-PodeWebSelect -Name Options + $options | + ConvertTo-PodeWebOption | + Update-PodeWebSelect -Name 'Example' } ) ``` -You can also optionally supply `-DisplayOptions` to alter the values displayed in the Select element, as well as also supply as `-SelectedValue`. - Various other properties can be updated as well. diff --git a/docs/Tutorials/Actions/Validation.md b/docs/Tutorials/Actions/Validation.md index 79582056..6b480481 100644 --- a/docs/Tutorials/Actions/Validation.md +++ b/docs/Tutorials/Actions/Validation.md @@ -27,7 +27,9 @@ New-PodeWebCard -Content @( } -Content @( New-PodeWebTextbox -Name 'Username' New-PodeWebTextbox -Name 'Password' -Type Password -PrependIcon Lock - New-PodeWebSelect -Name 'Role' -Options @('User', 'Admin') -Multiple + New-PodeWebSelect -Name 'Role' -Multiple -Options @( + @('User', 'Admin') | ConvertTo-PodeWebOption + ) ) ) ``` diff --git a/docs/Tutorials/ElementData.md b/docs/Tutorials/ElementData.md index 5abd62c8..6ab43483 100644 --- a/docs/Tutorials/ElementData.md +++ b/docs/Tutorials/ElementData.md @@ -12,7 +12,7 @@ New-PodeWebSelect -Name 'Random' -ScriptBlock { Get-Random -Minimum 1 -Maximum 10 }) - $options | Update-PodeWebSelect -Id $ElementData.Id + $options | ConvertTo-PodeWebOption | Update-PodeWebSelect -Id $ElementData.Id } ``` diff --git a/docs/Tutorials/Elements/Datalist.md b/docs/Tutorials/Elements/Datalist.md new file mode 100644 index 00000000..345021ef --- /dev/null +++ b/docs/Tutorials/Elements/Datalist.md @@ -0,0 +1,71 @@ +# Datalist + +| Support | | +| ------- | --- | +| Events | Yes | + +The Datalist element is a form input element, and can be added using [`New-PodeWebDatalist`](../../../Functions/Elements/New-PodeWebDatalist). This will add a textbox input element but with an "autocomplete" select-like dropdown menu, allowing the user to select either a predefined option or supply a custom value. + +## Options + +To create a Datalist element with 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 { + $value = $WebEvent.Data['DatalistExample'] + } -Content @( + New-PodeWebDatalist -Name 'DatalistExample' -Options @( + New-PodeWebOption -Name 'Text' + New-PodeWebOption -Name 'Xml' + New-PodeWebOption -Name 'Json' -Selected + New-PodeWebOption -Name 'Csv' + ) + ) +) +``` + +### Dynamic + +You can build a Datalist element's options dynamically by using the `-ScriptBlock` parameter. This will allow you to retrieve the options from elsewhere for use with the Datalist element. + +You can either return an array of raw values, or pipe the options into, and return, [`Update-PodeWebDatalist`](../../../Functions/Actions/Update-PodeWebDatalist). 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 Datalist 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-PodeWebDatalist -Name 'RawValues' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + # use New-PodeWebOption + New-PodeWebDatalist -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + New-PodeWebOption -Name (Get-Random -Minimum 1 -Maximum 10) + }) + + $options | Update-PodeWebDatalist -Id $ElementData.Id + } + + # use ConvertTo-PodeWebOption + New-PodeWebDatalist -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + }) + + $options | + ConvertTo-PodeWebOption | + Update-PodeWebDatalist -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`. diff --git a/docs/Tutorials/Elements/Form.md b/docs/Tutorials/Elements/Form.md index 6dfdee7a..0cd0da0a 100644 --- a/docs/Tutorials/Elements/Form.md +++ b/docs/Tutorials/Elements/Form.md @@ -21,7 +21,9 @@ New-PodeWebCard -Content @( New-PodeWebCredential -Name 'Credentials' New-PodeWebCheckbox -Name 'Checkboxes' -Options @('Terms', 'Privacy') -AsSwitch New-PodeWebRadio -Name 'Radios' -Options @('S', 'M', 'L') - New-PodeWebSelect -Name 'Role' -Options @('User', 'Admin', 'Operations') -Multiple + New-PodeWebSelect -Name 'Role' -Multiple -Options @( + @('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 605f55f6..742cd5ad 100644 --- a/docs/Tutorials/Elements/Modal.md +++ b/docs/Tutorials/Elements/Modal.md @@ -86,7 +86,9 @@ New-PodeWebCard -Content @( ) New-PodeWebModal -Name 'Edit Service' -AsForm -Content @( - New-PodeWebSelect -Name 'StartType' -Options Manual, Automatic, Disabled + New-PodeWebSelect -Name 'StartType' -Options @( + 'Manual', 'Automatic', 'Disabled' | ConvertTo-PodeWebOption + ) ) -ScriptBlock { Get-Service -Name $WebEvent.Data.Value | Set-Service -StartType $WebEvent.Data.StartType | Out-Null Show-PodeWebToast -Message "$($WebEvent.Data.Value) edited" diff --git a/docs/Tutorials/Elements/Select.md b/docs/Tutorials/Elements/Select.md index adf514c5..efa4829f 100644 --- a/docs/Tutorials/Elements/Select.md +++ b/docs/Tutorials/Elements/Select.md @@ -1,29 +1,42 @@ # Select -| Support | | -| ------- |-| -| Events | Yes | +| Support | | +| ------- | --- | +| Events | Yes | -The Select element is a form input element, and can be added using [`New-PodeWebSelect`](../../../Functions/Elements/New-PodeWebSelect). This will add a dropdown select menu to your form, allowing the user to select an entry; to allow multiple entries to be selected you can pass `-Multiple`, and to specify a pre-selected value you can use `-SelectedValue`. +The Select element is a form input element, and can be added using [`New-PodeWebSelect`](../../../Functions/Elements/New-PodeWebSelect). This will add a dropdown select menu to your form/page, allowing the user to select an entry - or multiple entries by passing `-Multiple`. ## Options -To create a Select element with pre-defined options, you can use the `-Options` parameter: +To create a Select element with pre-defined options, you can use the `-Options` parameter which accepts an array of either [`New-PodeWebOption`](../../../Functions/Elements/New-PodeWebOption) and/or [`New-PodeWebOptionGroup`](../../../Functions/Elements/New-PodeWebOptionGroup): ```powershell New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock { - $single = $WebEvent.Data['Single'] - $multiple = $WebEvent.Data['Multiple'] + $single = $WebEvent.Data['SingleExample'] + $multiple = $WebEvent.Data['MultipleExample'] } -Content @( - New-PodeWebSelect -Name 'Single' -Options 'Text', 'Xml', 'Json', 'Csv' -SelectedValue 'Json' - New-PodeWebSelect -Name 'Multiple' -Options 'Text', 'Xml', 'Json', 'Csv' -Multiple + New-PodeWebSelect -Name 'SingleExample' -Options @( + New-PodeWebOption -Name 'Text' + New-PodeWebOption -Name 'Xml' + New-PodeWebOption -Name 'Json' -Selected + New-PodeWebOption -Name 'Csv' + ) + + New-PodeWebSelect -Name 'MultipleExample' -Multiple -Options @( + New-PodeWebOptionGroup -Name 'Types' -Options ( + New-PodeWebOption -Name 'Text' + New-PodeWebOption -Name 'Xml' + New-PodeWebOption -Name 'Json' + New-PodeWebOption -Name 'Csv' + ) + ) ) ) ``` !!! Note - When using `-Multiple`, the values will be sent back in a comma separated list + When using `-Multiple`, the selected values will be sent back to the server as a comma-separated list Which looks like below: @@ -31,25 +44,40 @@ Which looks like below: ### Dynamic -You can build a Select element's options dynamically by using the `-ScriptBlock` parameter. This will allow you to retrieve the options from elsewhere and use them as options instead. +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). The following will both build a Select element with 10 random numbers as the options: +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). + +The following will both build a Select element with 10 random numbers as the options, using the above methods: ```powershell New-PodeWebCard -Content @( New-PodeWebForm -Name 'Example' -ScriptBlock { - New-PodeWebSelect -Name 'Random1' -ScriptBlock { - return @(foreach ($i in (1..10)) { + # return raw values for Pode.Web to convert + New-PodeWebSelect -Name 'RawValues' -ScriptBlock { + foreach ($i in (1..10)) { Get-Random -Minimum 1 -Maximum 10 + } + } + + # use New-PodeWebOption + New-PodeWebSelect -Name 'NewToUpdate' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { + New-PodeWebOption -Name (Get-Random -Minimum 1 -Maximum 10) }) + + $options | Update-PodeWebSelect -Id $ElementData.Id } - New-PodeWebSelect -Name 'Random2' -ScriptBlock { + # use ConvertTo-PodeWebOption + New-PodeWebSelect -Name 'NewToUpdate' -ScriptBlock { $options = @(foreach ($i in (1..10)) { Get-Random -Minimum 1 -Maximum 10 }) - $options | Update-PodeWebSelect -Id $ElementData.Id + $options | + ConvertTo-PodeWebOption | + Update-PodeWebSelect -Id $ElementData.Id } } ) @@ -62,7 +90,3 @@ 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`. - -## 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`. diff --git a/docs/Tutorials/Events.md b/docs/Tutorials/Events.md index 3cc2a3d2..3677bba4 100644 --- a/docs/Tutorials/Events.md +++ b/docs/Tutorials/Events.md @@ -29,7 +29,9 @@ Let's say you want to have a Select element, but not in a form. When the Select' ### Server-Side ```powershell -New-PodeWebSelect -Name 'Role' -Options @('Choose...', 'User', 'Admin', 'Operations') | +New-PodeWebSelect -Name 'Role' -Options @( + @('Choose...', 'User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) | Register-PodeWebEvent -Type Change -ScriptBlock { Show-PodeWebToast -Message "The value was changed: $($WebEvent.Data['Role'])" } @@ -40,7 +42,9 @@ If the element the event triggers for is a form input element, the value will be ### Client-Side ```powershell -New-PodeWebSelect -Name 'Role' -Options @('Choose...', 'User', 'Admin', 'Operations') | +New-PodeWebSelect -Name 'Role' -Options @( + @('Choose...', 'User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) | Register-PodeWebEvent -Type Change -JSFunction 'invokeCustomEvent' ``` diff --git a/examples/input_events.ps1 b/examples/input_events.ps1 index ff264589..1e1d4026 100644 --- a/examples/input_events.ps1 +++ b/examples/input_events.ps1 @@ -13,7 +13,9 @@ Start-PodeServer -Threads 2 { # select event $select = New-PodeWebContainer -Content @( New-PodeWebText -Value 'Please select a value: ' - New-PodeWebSelect -Name 'Bellows' -Options 'Bellow 1', 'Bellow 2', 'Bellow 3' | + New-PodeWebSelect -Name 'Bellows' -Options @( + @('Bellow 1', 'Bellow 2', 'Bellow 3') | ConvertTo-PodeWebOption + ) | Register-PodeWebEvent -Type Change -ScriptBlock { Open-PodeWebBellow -Name $WebEvent.Data['Bellows'] } @@ -43,7 +45,7 @@ Start-PodeServer -Threads 2 { Sort-Object -Property CPU -Descending | Select-Object -First 15 -Property Name, ID, WorkingSet, CPU | Update-PodeWebTable -Name 'Processes' - } + } New-PodeWebLine New-PodeWebTable -Name 'Processes' ) diff --git a/examples/inputs.ps1 b/examples/inputs.ps1 index 9ce6488c..b0d63e03 100644 --- a/examples/inputs.ps1 +++ b/examples/inputs.ps1 @@ -38,16 +38,43 @@ Start-PodeServer -Threads 2 { 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 - New-PodeWebRadio -Name 'Radios' -Options @('S', 'M', 'L') - New-PodeWebSelect -Name 'Role1' -Options @('Choose...', 'User', 'Admin', 'Operations') -PrependIcon 'account' -AppendIcon 'account' - New-PodeWebSelect -Name 'Role2' -Options @('User', 'Admin', 'Operations') -Multiple - New-PodeWebRange -Name 'Cores' -Value 30 -ShowValue + 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-PodeWebSelect -Name 'Role1' -PrependIcon 'account' -AppendIcon 'account' -HelpText 'Select a role' -Options @( + @('Choose...', 'User', 'Admin', 'Operations') | ConvertTo-PodeWebOption + ) + + New-PodeWebSelect -Name 'Role2' -Multiple -Options @( + New-PodeWebOptionGroup -Name 'General' -Options @( + New-PodeWebOption -Name 'User' + ) + New-PodeWebOptionGroup -Name 'Administrative' -Options @( + New-PodeWebOption -Name 'Admin' + New-PodeWebOption -Name 'Operations' -Selected + ) + ) + + New-PodeWebDatalist -Name 'Browsers' -Placeholder 'Select a browser' -Options @( + New-PodeWebOption -Name 'Chrome' + New-PodeWebOption -Name 'Firefox' + New-PodeWebOption -Name 'Edge' + New-PodeWebOption -Name 'Safari' -Selected + New-PodeWebOption -Name 'Opera' + ) + + New-PodeWebDatalist -Name 'Count' -Placeholder 'Select a value' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + + New-PodeWebRange -Name 'Cores' -Value 30 -ShowValue -HelpText 'Select a number of CPU cores' New-PodeWebSelect -Name 'Amount' -ScriptBlock { - return @(foreach ($i in (1..10)) { - Get-Random -Minimum 1 -Maximum 10 - }) + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } } | Register-PodeWebEvent -Type Change -ScriptBlock { Show-PodeWebToast -Message "The value was changed: $($WebEvent.Data['Amount'])" @@ -87,7 +114,7 @@ Start-PodeServer -Threads 2 { Get-Random -Minimum 1 -Maximum 10 }) - $options | Update-PodeWebSelect -Name 'DynamicSelect' + $options | ConvertTo-PodeWebOption | Update-PodeWebSelect -Name 'DynamicSelect' } New-PodeWebButton -Name 'Clear Options' -ScriptBlock { @@ -99,11 +126,35 @@ Start-PodeServer -Threads 2 { } New-PodeWebSelect -Name 'DynamicSelect' -Multiple -Size 6 -ScriptBlock { - return @(foreach ($i in (1..10)) { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } + } + ) + + $container3 = New-PodeWebContainer -Content @( + New-PodeWebButton -Name 'New List' -ScriptBlock { + $options = @(foreach ($i in (1..10)) { Get-Random -Minimum 1 -Maximum 10 }) + + $options | ConvertTo-PodeWebOption | Update-PodeWebDatalist -Name 'DynamicDatalist' + } + + New-PodeWebButton -Name 'Clear List' -ScriptBlock { + Clear-PodeWebDatalist -Name 'DynamicDatalist' + } + + New-PodeWebButton -Name 'Resync List' -ScriptBlock { + Sync-PodeWebDatalist -Name 'DynamicDatalist' + } + + New-PodeWebDatalist -Name 'DynamicDatalist' -ScriptBlock { + foreach ($i in (1..10)) { + Get-Random -Minimum 1 -Maximum 10 + } } ) - Add-PodeWebPage -Name 'Home' -Path '/' -Content $form, $container1, $modal, $container2 -Title 'Testing Inputs' -HomePage + Add-PodeWebPage -Name 'Home' -Path '/' -Content $form, $container1, $modal, $container2, $container3 -Title 'Testing Inputs' -HomePage } \ No newline at end of file diff --git a/src/Public/Actions.ps1 b/src/Public/Actions.ps1 index 5c8f0c9d..8d38748a 100644 --- a/src/Public/Actions.ps1 +++ b/src/Public/Actions.ps1 @@ -793,7 +793,7 @@ function Set-PodeWebSelect { [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] - $Value + $OptionName ) Send-PodeWebAction -Value @{ @@ -801,11 +801,11 @@ function Set-PodeWebSelect { ObjectType = 'Select' Name = $Name ID = $Id - Value = [System.Net.WebUtility]::HtmlEncode($Value) + OptionName = [System.Net.WebUtility]::HtmlEncode($OptionName) } } -function Update-PodeWebSelect { +function Add-PodeWebSelectOption { [CmdletBinding(DefaultParameterSetName = 'Name')] param( [Parameter(Mandatory = $true, ParameterSetName = 'Name')] @@ -817,19 +817,101 @@ function Update-PodeWebSelect { $Id, [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [string[]] - $Options, + [hashtable[]] + $Option, [Parameter()] + [string] + $GroupName + ) + + begin { + $items = @() + } + + process { + $items += $Option + } + + end { + # ensure options are only of type option or option-group + if ([string]::IsNullOrEmpty($GroupName)) { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option', 'Option-Group')) { + throw 'A Select can only contain Options or Option Groups' + } + } + else { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Select Option Group can only contain Options' + } + } + + Send-PodeWebAction -Value @{ + Operation = 'Add' + ObjectType = 'Select' + Name = $Name + ID = $Id + Options = $items + GroupName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'GroupName' -Value $GroupName) + } + } +} + +function Remove-PodeWebSelectOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] [string[]] - $DisplayOptions, + $OptionName, [Parameter()] [string[]] - $SelectedValue, + $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) + } +} + +function Update-PodeWebSelect { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] + [hashtable[]] + $Options, + + [Parameter()] + [ValidateSet(1, [int]::MaxValue)] + [int] + $Size, [switch] - $Disabled + $Disabled, + + [switch] + $Multiple ) begin { @@ -841,15 +923,19 @@ function Update-PodeWebSelect { } end { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option', 'Option-Group')) { + throw 'A Select can only contain Options or Option Groups' + } + Send-PodeWebAction -Value @{ - Operation = 'Update' - ObjectType = 'Select' - Name = $Name - ID = $Id - Options = $items - DisplayOptions = @(Protect-PodeWebValues -Value $DisplayOptions -Default $items -EqualCount) - SelectedValue = @(Protect-PodeWebValues -Value $SelectedValue -Encode) - Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) + Operation = 'Update' + ObjectType = 'Select' + Name = $Name + ID = $Id + Options = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Options' -Value $items) + Size = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Size' -Value $Size) + Multiple = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Multiple' -Value $Multiple.IsPresent) + Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) } } } @@ -894,6 +980,195 @@ function Sync-PodeWebSelect { } } +function Set-PodeWebDatalist { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string] + $Value + ) + + Send-PodeWebAction -Value @{ + Operation = 'Set' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + Value = [System.Net.WebUtility]::HtmlEncode($Value) + } +} + +function Add-PodeWebDatalistOption { + [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, + + [switch] + $NoAutoSelect + ) + + begin { + $items = @() + } + + process { + $items += $Option + } + + end { + # ensure options are only of type option + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Datalist can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Add' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + Options = $items + NoAutoSelect = $NoAutoSelect.IsPresent + } + } +} + +function Remove-PodeWebDatalistOption { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] + [string[]] + $OptionName, + + [switch] + $NoAutoSelect + ) + + Send-PodeWebAction -Value @{ + Operation = 'Remove' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + OptionName = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'OptionName' -Value $OptionName) + NoAutoSelect = $NoAutoSelect.IsPresent + } +} + +function Update-PodeWebDatalist { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id, + + [Parameter(ValueFromPipeline = $true)] + [hashtable[]] + $Options, + + [switch] + $ReadOnly, + + [switch] + $Disabled, + + [switch] + $NoAutoSelect + ) + + begin { + $items = @() + } + + process { + $items += $Options + } + + end { + if (!(Test-PodeWebContent -Content $items -ComponentType Element -ObjectType 'Option')) { + throw 'A Datalist can only contain Options' + } + + Send-PodeWebAction -Value @{ + Operation = 'Update' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + Options = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Options' -Value $items) + ReadOnly = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'ReadOnly' -Value $ReadOnly.IsPresent) + Disabled = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'Disabled' -Value $Disabled.IsPresent) + NoAutoSelect = (Test-PodeWebParameter -Parameters $PSBoundParameters -Name 'NoAutoSelect' -Value $NoAutoSelect.IsPresent) + } + } +} + +function Clear-PodeWebDatalist { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Clear' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + } +} + +function Sync-PodeWebDatalist { + [CmdletBinding(DefaultParameterSetName = 'Name')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] + [string] + $Name, + + [Parameter(Mandatory = $true, ParameterSetName = 'Id')] + [string] + $Id + ) + + Send-PodeWebAction -Value @{ + Operation = 'Sync' + ObjectType = 'Datalist' + Name = $Name + ID = $Id + } +} + function Update-PodeWebBadge { [CmdletBinding()] param( diff --git a/src/Public/Elements.ps1 b/src/Public/Elements.ps1 index 2d5a0566..993320bd 100644 --- a/src/Public/Elements.ps1 +++ b/src/Public/Elements.ps1 @@ -379,6 +379,10 @@ function New-PodeWebCheckbox { [string[]] $DisplayOptions, + [Parameter()] + [string] + $HelpText, + [Parameter(ParameterSetName = 'Multiple')] [switch] $Inline, @@ -415,6 +419,7 @@ function New-PodeWebCheckbox { 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 @@ -446,6 +451,10 @@ function New-PodeWebRadio { [string[]] $DisplayOptions, + [Parameter()] + [string] + $HelpText, + [switch] $Inline, @@ -471,6 +480,7 @@ function New-PodeWebRadio { 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 @@ -493,13 +503,9 @@ function New-PodeWebSelect { $Id, [Parameter(ParameterSetName = 'Options')] - [string[]] + [hashtable[]] $Options, - [Parameter(ParameterSetName = 'Options')] - [string[]] - $DisplayOptions, - [Parameter(ParameterSetName = 'ScriptBlock')] [scriptblock] $ScriptBlock, @@ -509,10 +515,7 @@ function New-PodeWebSelect { $ArgumentList, [Parameter()] - [string[]] - $SelectedValue, - - [Parameter()] + [ValidateRange(1, [int]::MaxValue)] [int] $Size = 4, @@ -532,6 +535,10 @@ function New-PodeWebSelect { [string] $AppendIcon, + [Parameter()] + [string] + $HelpText, + [switch] $Multiple, @@ -545,16 +552,41 @@ function New-PodeWebSelect { $HideName ) - if (!$Multiple.IsPresent -and $SelectedValue.Length -ge 2) { - throw 'Multiple selected values require -Multiple switch' + # ensure options are only of type option or option-group + if (!(Test-PodeWebContent -Content $Options -ComponentType Element -ObjectType 'Option', 'Option-Group')) { + throw 'A Select can only contain Options or Option Groups' } - $Id = Get-PodeWebElementId -Tag Select -Id $Id -Name $Name + # only allow multiple selected values if -Multiple is set + if (!$Multiple) { + $selectedCount = 0 + + foreach ($option in $Options) { + if ($option.Selected) { + $selectedCount++ + continue + } + + if ($option.ObjectType -ine 'Option-Group') { + continue + } - if ($Size -le 0) { - $Size = 4 + foreach ($groupOption in $option.Options) { + if ($groupOption.Selected) { + $selectedCount++ + } + } + } + + if ($selectedCount -ge 2) { + throw 'Multiple selected options require -Multiple switch' + } } + # generate an ID + $Id = Get-PodeWebElementId -Tag Select -Id $Id -Name $Name + + # build element $element = @{ Operation = 'New' ComponentType = 'Element' @@ -563,12 +595,11 @@ function New-PodeWebSelect { DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) HideName = $HideName.IsPresent ID = $Id - Options = @($Options) - DisplayOptions = @(Protect-PodeWebValues -Value $DisplayOptions -Default $Options -EqualCount -Encode) + Options = $Options IsDynamic = ($null -ne $ScriptBlock) - SelectedValue = $SelectedValue Multiple = $Multiple.IsPresent Size = $Size + HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) Prepend = @{ Enabled = (![string]::IsNullOrWhiteSpace($PrependText) -or ![string]::IsNullOrWhiteSpace($PrependIcon)) Text = $PrependText @@ -636,7 +667,203 @@ function New-PodeWebSelect { } if ($result.Length -gt 0) { - $result = ($result | Update-PodeWebSelect -Id $ElementData.ID) + $result = $result | + ConvertTo-PodeWebOption | + Update-PodeWebSelect -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-PodeWebDatalist { + [CmdletBinding(DefaultParameterSetName = 'Options')] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [string] + $DisplayName, + + [Parameter()] + [string] + $Id, + + [Parameter(ParameterSetName = 'Options')] + [hashtable[]] + $Options, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, + + [Parameter()] + [string] + $Placeholder, + + [Parameter()] + [string] + $Width = 100, + + [Parameter()] + [string] + $PrependText, + + [Parameter()] + [string] + $PrependIcon, + + [Parameter()] + [string] + $AppendText, + + [Parameter()] + [string] + $AppendIcon, + + [Parameter()] + [string] + $HelpText, + + [switch] + $ReadOnly, + + [switch] + $Required, + + [switch] + $Disabled, + + [switch] + $HideName, + + [switch] + $NoAutoSelect + ) + + # ensure options are only of type option + if (!(Test-PodeWebContent -Content $Options -ComponentType Element -ObjectType 'Option')) { + throw 'A Datalist can only contain Options' + } + + # error if multiple selected values + $selectedCount = 0 + + foreach ($option in $Options) { + if ($option.Selected) { + $selectedCount++ + } + } + + if ($selectedCount -ge 2) { + throw 'Datalist cannot have multiple selected options' + } + + # generate an ID + $Id = Get-PodeWebElementId -Tag Datalist -Id $Id -Name $Name + + # build element + $element = @{ + Operation = 'New' + ComponentType = 'Element' + ObjectType = 'Datalist' + Name = $Name + DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) + HideName = $HideName.IsPresent + ID = $Id + Options = $Options + IsDynamic = ($null -ne $ScriptBlock) + Placeholder = $Placeholder + Width = (ConvertTo-PodeWebSize -Value $Width -Default 'auto' -Type '%') + HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) + Prepend = @{ + Enabled = (![string]::IsNullOrWhiteSpace($PrependText) -or ![string]::IsNullOrWhiteSpace($PrependIcon)) + Text = $PrependText + Icon = $PrependIcon + } + Append = @{ + Enabled = (![string]::IsNullOrWhiteSpace($AppendText) -or ![string]::IsNullOrWhiteSpace($AppendIcon)) + Text = $AppendText + Icon = $AppendIcon + } + NoAuthentication = $NoAuthentication.IsPresent + NoAutoSelect = $NoAutoSelect.IsPresent + ReadOnly = $ReadOnly.IsPresent + Required = $Required.IsPresent + Disabled = $Disabled.IsPresent + } + + $routePath = "/pode.web-dynamic/elements/datalist/$($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-PodeWebDatalist -Id $ElementData.ID } $result = Join-PodeWebDynamicOutput -Wrapped $wrapped -Output $result @@ -653,6 +880,101 @@ function New-PodeWebSelect { return $element } +function New-PodeWebOptionGroup { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [string] + $DisplayName, + + [Parameter(Mandatory = $true)] + [hashtable[]] + $Options, + + [switch] + $Disabled + ) + + if (!(Test-PodeWebContent -Content $Options -ComponentType Element -ObjectType Option)) { + throw 'An Option Group can only contain Options' + } + + return @{ + Operation = 'New' + ComponentType = 'Element' + ObjectType = 'Option-Group' + Name = (Protect-PodeWebValue -Value $Name -Encode) + DisplayName = (Protect-PodeWebValue -Value $DisplayName -Default $Name -Encode) + Options = $Options + Disabled = $Disabled.IsPresent + } +} + +function New-PodeWebOption { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [string] + $DisplayName, + + [Parameter()] + [string] + $Label, + + [switch] + $Disabled, + + [switch] + $Selected + ) + + 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 + } +} + +function ConvertTo-PodeWebOption { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string[]] + $InputObject, + + [Parameter()] + [string[]] + $SelectedOption + ) + + begin { + $items = @() + } + + process { + $items += $InputObject + } + + end { + foreach ($item in $items) { + New-PodeWebOption -Name $item -Selected:($item -iin $SelectedOption) + } + } +} + function New-PodeWebRange { [CmdletBinding()] param( @@ -685,6 +1007,10 @@ function New-PodeWebRange { [double] $Step = 1.0, + [Parameter()] + [string] + $HelpText, + [switch] $Disabled, @@ -737,6 +1063,7 @@ function New-PodeWebRange { Min = $Min Max = $Max Step = $Step + HelpText = [System.Net.WebUtility]::HtmlEncode($HelpText) Disabled = $Disabled.IsPresent ShowValue = $ShowValue.IsPresent Required = $Required.IsPresent diff --git a/src/Public/Pages.ps1 b/src/Public/Pages.ps1 index 84bcf8cc..318e107b 100644 --- a/src/Public/Pages.ps1 +++ b/src/Public/Pages.ps1 @@ -921,11 +921,15 @@ function ConvertTo-PodeWebPage { if ($param.Attributes.TypeId.Name -icontains 'ValidateSetAttribute') { $values = ($param.Attributes | Where-Object { $_.TypeId.Name -ieq 'ValidateSetAttribute' }).ValidValues - New-PodeWebSelect -Name "$($name)_$($param.Name)" -DisplayName $param.Name -Options $values -SelectedValue $default -Multiple:$multiple + New-PodeWebSelect -Name "$($name)_$($param.Name)" -DisplayName $param.Name -Multiple:$multiple -Options @( + $values | ConvertTo-PodeWebOption -SelectedValue $default + ) } elseif ($param.ParameterType.BaseType.Name -ieq 'enum') { $values = [enum]::GetValues($param.ParameterType) - New-PodeWebSelect -Name "$($name)_$($param.Name)" -DisplayName $param.Name -Options $values -SelectedValue $default -Multiple:$multiple + New-PodeWebSelect -Name "$($name)_$($param.Name)" -DisplayName $param.Name -Multiple:$multiple -Options @( + $values | ConvertTo-PodeWebOption -SelectedValue $default + ) } else { New-PodeWebTextbox -Name "$($name)_$($param.Name)" -DisplayName $param.Name -Value $default diff --git a/src/Templates/Public/scripts/templates.js b/src/Templates/Public/scripts/templates.js index 2a2cc5a2..4099387f 100644 --- a/src/Templates/Public/scripts/templates.js +++ b/src/Templates/Public/scripts/templates.js @@ -859,7 +859,6 @@ class PodeElement { // check focus state if (obj.focusable && (evt == 'focus' || evt == 'focusout') && obj.previouslyFocused === obj.focused) { skip = true; - console.log(`focus: ${obj.focused}, skip event ${evt}`); } // stop propagation/prevent default @@ -1680,7 +1679,7 @@ class PodeFormElement extends PodeContentElement { // help text if (this.help.enabled && this.help.text) { - html += `${this.help.text}`; + html += `${this.help.text}`; } // are we in a form? @@ -1774,6 +1773,53 @@ class PodeFormElement extends PodeContentElement { } } +class PodeFormOptionElement extends PodeFormElement { + constructor(data, sender, opts) { + super(data, sender, opts); + } + + buildOptions(options) { + var html = ''; + + convertToArray(options).forEach((opt) => { + if (opt.Options) { + html += this.buildGroup(opt); + } + else { + html += this.buildOption(opt); + } + }); + + return html; + } + + buildOption(option) { + var label = option.Label ? ` label='${option.Label}'` : ''; + + return ``; + } + + buildGroup(group) { + var html = ``; + + convertToArray(group.Options).forEach((opt) => { + html += this.buildOption(opt); + }); + + html += ``; + return html; + } +} + class PodeFormMultiElement extends PodeFormElement { constructor(data, sender, opts) { super(data, sender, opts); @@ -5315,35 +5361,21 @@ class PodeHidden extends PodeFormElement { } PodeElementFactory.setClass(PodeHidden); -class PodeSelect extends PodeFormElement { +class PodeSelect extends PodeFormOptionElement { static type = 'select'; constructor(data, sender, opts) { super(data, sender, opts); - this.multiSelect = data.Multiple ?? false; + this.multiple = data.Multiple ?? false; } new(data, sender, opts) { - var multiple = this.multiSelect ? `multiple size='${data.Size ?? 1}'` : ''; - - var selectedValue = convertToArray(data.SelectedValue); - if (!this.multiSelect && selectedValue.length >= 2) { - selectedValue = [selectedValue[0]]; - } + var multiple = this.multiple ? `multiple size='${data.Size ?? 1}'` : ''; var options = ''; - data.DisplayOptions = convertToArray(data.DisplayOptions); - convertToArray(data.Options).forEach((opt, index) => { - if (!opt) { - return; - } - - options += ``; - }); + if (data.Options) { + options = this.buildOptions(data.Options); + } return ``; + + var datalist = ` + ${options} + `; + + return `${input}${datalist}`; + } load(data, sender, opts) { super.load(data, sender, opts); + + // load options dynamically if (this.dynamic) { sendAjaxReq(`${this.url}/options`, null, this, true); } + + // if there is a selected option, set element value + if (!this.dynamic && !data.NoAutoSelect) { + this.checkSelected(); + } } set(data, sender, opts) { @@ -5373,34 +5528,61 @@ class PodeSelect extends PodeFormElement { } this.element.val(decodeHTML(data.Value)); + this.trigger('change'); } update(data, sender, opts) { super.update(data, sender, opts); // update options - data.Options = convertToArray(data.Options); - if (data.Options.length > 0) { + if (data.Options != null) { this.clear(); + this.getDatalist().append(this.buildOptions(data.Options)); - data.DisplayOptions = convertToArray(data.DisplayOptions); - data.SelectedValue = convertToArray(data.SelectedValue); - - data.Options.forEach((opt, index) => { - this.element.append(``); - }); + if (!data.NoAutoSelect) { + this.checkSelected(); + } } } clear(data, sender, opts) { - this.element.empty(); + this.getDatalist().empty(); + this.element.val(''); + } + + add(data, sender, opts) { + // build and add options + var html = this.buildOptions(data.Options); + this.getDatalist().append(html); + + if (!data.NoAutoSelect) { + this.checkSelected(); + } + } + + remove(data, sender, opts) { + // remove any options + convertToArray(data.Value).forEach((opt) => { + this.getDatalist().find(`option[value='${opt}']`).remove(); + }); + + if (!data.NoAutoSelect) { + this.checkSelected(); + } + } + + getDatalist() { + return $(`datalist[for='${this.uuid}']`); + } + + checkSelected() { + var selected = this.getDatalist().find('option[selected]'); + if (selected && selected.length > 0) { + this.element.val(selected.attr('value')); + } } } -PodeElementFactory.setClass(PodeSelect); +PodeElementFactory.setClass(PodeDatalist); class PodeRange extends PodeFormElement { static type = 'range';