Support multiple users editing the same content in an editor.
This would be similar to the editor in something like confluence or online Word.
Regards
I'm trying to use the Blazor editor but the "Insert Image" function is very basic. Previously using Webforms & Core the insert image in the editor presented the user with a file management and image upload popup. Is this functionality available with the Blazor component?
=====ADMIN EDIT=====
Possible alternative:
I would like the following behavior in the date picker. Perhaps it can be achieved with a parameter similar to the one in UI for ASP.NET AJAX - the FocusedDate property (something like RadDatePicker1.FocusedDate = DateTime.Today.AddDays(30);).
The current behaviour is:
* if date is set, show month of set date
*else show month of current date (today)
Desired behaviour:
* if date is set, show month of set date
* else if min date's month is greater than current date month then show month of min date
* else show month of current date
Here is a code snippet to illustrate the issue:
Open the picker - it will start in the month with today's date, but I want it to start at the min date - one month later
<TelerikDatePicker @bind-Value="@theDate" Min="@minDate" Max="@maxDate"></TelerikDatePicker>
@code {
DateTime theDate = DateTime.Now;
DateTime minDate = DateTime.Now.AddDays(60);
DateTime maxDate = DateTime.Now.AddDays(230);
}===
The NumericTextBox does not render the new Value that is set in ValueChanged if this new value is different than the event argument. Instead, the component clears the textbox, even though the component Value parameter is correct.
https://blazorrepl.telerik.com/wzaAHYas221go9xd48
The problem occurs only if there is an existing value and the user removes it with Backspace.
Window, Dialog, and other popups render as children of TelerikRootComponent, which makes CSS Isolation difficult. Current strategy says to use containment selector or global scope css styles.
To allow CSS isolation to pass through, I'd like to see an additional "CssScope" attribute. For example:
<TelerikWindow CssScope="my-custom-scope">
</TelerikWindow>Then when the window is rendered:
<div ... my-custom-scope>
....
</div>
(bug report only)
Regression introduced in 13.0.0.
1. Run this example: https://blazorrepl.telerik.com/wUuSGClv00orFeRm59
(optional)
The Size, Rounded, FillMode parameters are ignored.
The parameter values should apply and change the appearance of the SearchBox.
Hi,
Using VS code I tried to create a project using the latest Telerik UI for Blazor Productivity tools. Updated 24-12-03
The Error:
I would like to be able to disable one or more radio buttons in the RadioGroup.
=====
TELERIK EDIT
In the meantime, consider the following workaround, which relies on CSS and RadioGroup ItemTemplate.
<h4>Default Value Disabled</h4>
RadioGroupValue1: @RadioGroupValue1
<TelerikRadioGroup Data="@Options"
@bind-Value="@RadioGroupValue1"
ValueField="@nameof(RadioModel.Id)"
TextField="@nameof(RadioModel.Text)">
<ItemTemplate>
@{
var dataItem = (RadioModel)context;
var isChecked = dataItem.Id == RadioGroupValue1;
}
@if (!dataItem.Enabled)
{
<span class="disabled-radio-option">
<span class="k-radio-wrap">
<input type="radio" value="@dataItem.Id" aria-checked="@isChecked" aria-disabled="true" aria-invalid="true"
class="@( $"fake k-radio k-radio-md {(isChecked ? "k-checked" : "")}" )" style="user-select: none;" />
</span>
<span>@dataItem.Text</span>
</span>
}
else
{
<span>@dataItem.Text</span>
}
</ItemTemplate>
</TelerikRadioGroup>
<h4>No Default Value</h4>
RadioGroupValue2: @RadioGroupValue2
<TelerikRadioGroup Data="@Options"
@bind-Value="@RadioGroupValue2"
ValueField="@nameof(RadioModel.Id)"
TextField="@nameof(RadioModel.Text)">
<ItemTemplate>
@{
var dataItem = (RadioModel)context;
var isChecked = dataItem.Id == RadioGroupValue2;
}
@if (!dataItem.Enabled)
{
<span class="disabled-radio-option">
<span class="k-radio-wrap">
<input type="radio" value="@dataItem.Id" aria-checked="@isChecked" aria-disabled="true" aria-invalid="true"
class="@( $"fake k-radio k-radio-md {(isChecked ? "k-checked" : "")}" )" style="user-select: none;" />
</span>
<span>@dataItem.Text</span>
</span>
}
else
{
<span>@dataItem.Text</span>
}
</ItemTemplate>
</TelerikRadioGroup>
<style>
.k-radio-list-item:has(.disabled-radio-option) {
pointer-events: none;
cursor: default;
opacity: 0.6;
gap: 0;
}
.disabled-radio-option {
display: flex;
align-items: center;
align-content: center;
gap: var(--kendo-spacing-1);
}
.k-radio-list-item:has(.disabled-radio-option) input:not(.fake) {
display: none;
}
</style>
@code{
private int RadioGroupValue1 { get; set; } = 2;
private int RadioGroupValue2 { get; set; }
private List<RadioModel> Options { get; set; } = new List<RadioModel>
{
new RadioModel { Id = 1, Text = "Option 1" },
new RadioModel { Id = 2, Text = "Option 2", Enabled = false },
new RadioModel { Id = 3, Text = "Option 3" },
new RadioModel { Id = 4, Text = "Option 4", Enabled = false },
new RadioModel { Id = 5, Text = "Option 5" },
};
public class RadioModel
{
public int Id { get; set; }
public string Text { get; set; } = string.Empty;
public bool Enabled { get; set; } = true;
}
}
With Blazor server, clicking the NOW button (obviously) sets the time to the time on the server since that's where the code is running. Is there a way to trap the NOW button click or somehow give it an offset or define the value that NOW means so NOW will mean the time that the user is sitting in?
---
TELERIK EDIT
In the meantime, here are a few possible workarounds:
1. Use the DateTimePicker's ValueChanged event to detect new values that are very close or match the server's current DateTime. In such cases, you can assume that the user has clicked on TODAY / NOW, and set the local user time as a component value.
Server Time on UI Refresh: @DateTime.Now.ToLongTimeString()
<br />
User Local Time on Page Load: @LocalTime?.ToLongTimeString()
<br />
User Local Time Offset: @LocalOffset
<br />
<br />
<TelerikDateTimePicker Value="@PickerValue"
ValueChanged="@( (DateTime? newValue) => PickerValueChanged(newValue) )"
ValueExpression="@( () => LocalTime )"
Format="yyyy-MMM-dd HH:mm:ss"
Width="240px" />
<!-- Move JS code to a separate JS file in production -->
<script suppress-error="BL9992">
function getLocalTime() {
var d = new Date();
return d.getTimezoneOffset();
}
</script>
@code {
private DateTime? PickerValue { get; set; }
private DateTime? LocalTime { get; set; }
private int LocalOffset { get; set; }
private void PickerValueChanged(DateTime? newValue)
{
DateTime serverNow = DateTime.Now;
DateTime utcNow = DateTime.UtcNow;
TimeSpan nowTolerance = new TimeSpan(0, 0, 10);
if (newValue - serverNow < nowTolerance)
{
PickerValue = utcNow.AddMinutes(-LocalOffset);
}
else
{
PickerValue = newValue;
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
LocalOffset = await js.InvokeAsync<int>("getLocalTime");
LocalTime = DateTime.UtcNow.AddMinutes(-LocalOffset);
StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
}
2. Use the DatePicker's HeaderTemplate with a custom TODAY / NOW button.
3. Hide the NOW button with CSS:
<style>
.no-now-button .k-time-now {
display: none;
}
</style>
<TelerikDateTimePicker @bind-Value="@PickerValue"
Format="yyyy-MMM-dd HH:mm:ss"
PopupClass="no-now-button"
Width="240px" />
@code {
private DateTime? PickerValue { get; set; }
}
I would like to see icon support for the switch control. See the attached screen shot for the use case.
Could you include the OnHide event in the TelerikPopover, similar to the Popup component? The Popover already hides on outside clicks, but we have no method to programatically react to it, for example to reset input values and treat it as a cancellation.
===
TELERIK EDIT: In the meantime, a possible workaround is to detect clicks outside the Popover with JavaScript:
https://blazorrepl.telerik.com/QKOBbSbF25rgdsgP20
@implements IAsyncDisposable
@inject IJSRuntime JS
<TelerikPopover @ref="@PopoverRef"
AnchorSelector=".popover-target"
ShowOn="@PopoverShowOn.Click"
Position="@PopoverPosition.Right"
Offset="20"
Class="my-popover">
<PopoverContent>
Telerik Popover for Blazor
</PopoverContent>
</TelerikPopover>
<p>Popover Hide Log: @PopoverOnHideLog</p>
<TelerikButton Class="popover-target" OnClick="ShowPopover">
Show Popover
</TelerikButton>
<script suppress-error="BL9992">
var dotnetRef;
window.registerOutsideClick = function (dotnetHelper) {
dotnetRef = dotnetHelper;
document.addEventListener("click", onPopoverOutsideClick);
};
window.unregisterOutsideClick = function () {
dotnetRef = null;
document.removeEventListener("click", onPopoverOutsideClick);
};
function onPopoverOutsideClick(e) {
window.setTimeout(() => {
const popover = document.querySelector(".my-popover");
const anchor = document.querySelector(".popover-target");
const clickedInsidePopover = popover?.contains(e.target);
const clickedAnchor = anchor?.contains(e.target);
if (!clickedInsidePopover && !clickedAnchor) {
dotnetRef?.invokeMethodAsync("OnPopoverHide");
}
}, 100);
}
</script>
@code {
#nullable enable
private DotNetObjectReference<__Main>? DotNetRef { get; set; }
private TelerikPopover? PopoverRef;
private string PopoverOnHideLog { get; set; } = string.Empty;
private void ShowPopover()
{
PopoverRef?.Show();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("registerOutsideClick", DotNetRef);
}
}
protected override void OnInitialized()
{
DotNetRef = DotNetObjectReference.Create(this);
}
[JSInvokable]
public void OnPopoverHide()
{
PopoverOnHideLog = $"Popover closed automatically at {DateTime.Now.ToString("HH:mm:ss")}";
StateHasChanged();
}
public async ValueTask DisposeAsync()
{
if (DotNetRef != null)
{
DotNetRef.Dispose();
}
await JS.InvokeVoidAsync("unregisterOutsideClick");
}
}
The AutoComplete component does not update the category grouping header, even when the data within that group has been removed. As a result, the outdated group header remains visible until you scroll down to another group, at which point the first group header refreshes and disappears as expected.
Repro: REPL link.
The Grid performance worsens progressively with each subsequent edit operation. Please optimize that.
Test page: https://blazorrepl.telerik.com/mJuHFNbb17FpJu9b54
Click on a Price or Quantity cell to start edit mode and tab repetitively to observe the degradation.
The disable option is still showing the step and it is not good for me. I have 35 types of transactions and all of them have generic and specific step.
---
ADMIN EDIT
Here is a potential workaround - basic conditional markup can add or remove steps. The key thing is that they will be added to the end of the wizard if they had not been rendered. To handle this, dispose and re-initialize the component, so the step will be rendered at the correct position.
If you have complex steps, you can work around this by creating a collection of descriptor models for the list of steps and create the steps based on that collection, where you can keep the VIsible flag, in a fashion similar to this example for the TabStrip.
<TelerikButton OnClick="@ToggleStep">Toggle attachments step visibility</TelerikButton>
@if (WizardVisible)
{
<TelerikWizard @bind-Value="@CurrStepIndex">
<WizardSteps>
<WizardStep Label="Personal Details" Icon="SvgIcon.User">
<Content>
content here
</Content>
</WizardStep>
@if (AttachmentsStepVisible)
{
<WizardStep Label="Attachments" Icon="SvgIcon.Paperclip">
<Content>
conditional content here
</Content>
</WizardStep>
}
<WizardStep Label="Confirmation" Icon="SvgIcon.Check">
<Content>
other content here
</Content>
</WizardStep>
</WizardSteps>
</TelerikWizard>
}
@code {
private bool AttachmentsStepVisible { get; set; }
private bool WizardVisible { get; set; } = true;
private int CurrStepIndex { get; set; }
private async void ToggleStep()
{
//dispose the Wizard
WizardVisible = false;
// defence against hiding the step when it is the last step, which would cause an exception
if (AttachmentsStepVisible && CurrStepIndex == 2)
{
CurrStepIndex = 1;
}
//the actual visibility toggle
AttachmentsStepVisible = !AttachmentsStepVisible;
//allow some time for the disposal and toggling the step visibility prior to re-initialization
await Task.Delay(10);
//re-initialize the Wizard
WizardVisible = true;
StateHasChanged();
}
}---
The CheckBoxList filter does not work as expected when the Grid is bound to ExpandoObject
===
ADMIN EDIT: A possible workaround is to bind the Grid with OnRead event and populate the MemberType property of the filter descriptors manually:
@using System.Dynamic
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid OnRead="@OnGridRead"
TItem="@ExpandoObject"
Pageable="true"
Sortable="true"
FilterMode="@GridFilterMode.FilterMenu"
FilterMenuType="@FilterMenuType.CheckBoxList"
Height="400px">
<GridToolBarTemplate>
<GridSearchBox />
</GridToolBarTemplate>
<GridColumns>
@{
if (GridData != null && GridData.Any())
{
<GridColumn Field="PropertyInt" FieldType="@GridPropertyTypes["PropertyInt"]" />
<GridColumn Field="PropertyString" FieldType="@GridPropertyTypes["PropertyString"]" />
<GridColumn Field="PropertyGroup" FieldType="@GridPropertyTypes["PropertyString"]" />
<GridColumn Field="PropertyDate" FieldType="@GridPropertyTypes["PropertyDate"]" />
<GridColumn Field="PropertyBool" FieldType="@GridPropertyTypes["PropertyBool"]" />
}
}
</GridColumns>
</TelerikGrid>
@code {
private List<ExpandoObject> GridData { get; set; } = new List<ExpandoObject>();
private Dictionary<string, Type> GridPropertyTypes { get; set; } = new Dictionary<string, Type>() {
{ "Id", typeof(int) },
{ "PropertyInt", typeof(int) },
{ "PropertyString", typeof(string) },
{ "PropertyGroup", typeof(string) },
{ "PropertyDate", typeof(DateTime) },
{ "PropertyBool", typeof(bool) }
};
private async Task OnGridRead(GridReadEventArgs args)
{
args.Request.Filters.OfType<CompositeFilterDescriptor>()
.Each(x =>
{
x.FilterDescriptors.OfType<FilterDescriptor>()
.Each(y => y.MemberType = GridPropertyTypes[y.Member]);
});
var result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override void OnInitialized()
{
for (int i = 1; i <= 18; i++)
{
dynamic expando = new ExpandoObject();
expando.Id = i;
expando.PropertyGroup = $"Group {(i % 3 + 1)}";
expando.PropertyInt = i;
expando.PropertyString = $"String {(char)(64 + i)}{(char)(64 + i)}";
expando.PropertyDate = DateTime.Now.AddMonths(-i);
expando.PropertyBool = i % 2 != 0;
GridData.Add(expando);
}
}
}