mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-14 17:01:18 +08:00
e8965eb369
* feat(module: table): add a method to get query model (#1202) * feat(module: table): add OnExpand event (#1208) * feat(module: table): add summary (#1218) * feat(module: table): add summary row * feat: add fixed summary cell * feat(module: InputNumber): Add long-click and keyboard operation (#1235) * feat(module: InputNumber): Add long click and keyboard event 1. When hold mouse down button will always trigger the increase or decrease 2. Add event listener to "ArrowUp" and "ArrowDown" key * feat(module: InputNumber): Add long click and keyboard event 1. When hold mouse down button will always trigger the increase or decrease 2. Add event listener to "ArrowUp" and "ArrowDown" key * fix null reference exception Co-authored-by: ElderJames <shunjiey@hotmail.com> * refactor(module: pagination): cover the apis of react (#1220) * feat(module: config-provider): support RTL (#1238) * feat(module: config-provider): support RTL * add rtl for each component * fix rtl for pagination * add rtl for overlay * chore: sync ant-design v4.14.0 (#1249) * feat(module: tag): add closing event (#1268) * tag add onclosing event * clean code * translate the comment * translation Co-authored-by: James Yeung <shunjiey@hotmail.com> * chore: sync ant-design v4.15.0 (#1281) * feat: add built-in themes (#1286) * feat: add themes * add docs theming styles * fix the RTL for FixedWidgets * fix aliyun theme * fix docs style * feat(module: form): Form lable supports left alignment (#1292) * feat: Form lable supports left alignment * Update and rename components/grid/Col.razor.cs to 组件/格网/Col.razor.cs * Delete Col.razor.cs * no message * PR:1292 * typo LabelAlign * add the property in Col instead FormItem * clean code Co-authored-by: James Yeung <shunjiey@hotmail.com> * feat(module: upload): missing and new event (#1302) * feat(module:upload): missing and new event * docs(module:upload): new events (missing Chinese) Co-authored-by: James Yeung <shunjiey@hotmail.com> * refactor: unified use of FeedbackComponent for modal comfirm and drawer (#1263) * refactor: support to use the same template for confirm and modal * refactor: support to use the same template for drawer * refactor: separate interface IOkCancelRef * chore: modify EventUtil class summary Co-authored-by: James Yeung <shunjiey@hotmail.com> * feat(module: alert): add message template and loop banner demo (#1250) * feat(Alert): added alert loop component * fix: move cmp into main * feat: add parameters for looping text * feat: add new messages loop * fix: create new internal looptext cmp * doc: add demo * doc: add demo markdown * doc: update alert api * doc: update cn docs * fix: add missing dependency * fix: update param name * impleement loop text with css * fix the document Co-authored-by: James Yeung <shunjiey@hotmail.com> * feat(module: table): add support for Display attribute (#1310) feat(module:table): add support for Display attribute Display attribute is widely used to specify display text for entity properties. Table component should get column names from Display attribute instances. Closes #1278 * test: add TestKit for public tests (#1248) * test: change folder structure and add new TestKit csproj for public testing of AntDesign-based applications * docs: added CN and EN docs about TestKit * fix doc translate * change the directory structure Co-authored-by: Patryk Grzelak <pgrzelak@mutate.app> Co-authored-by: James Yeung <shunjiey@hotmail.com> * feat(module: space): add wrap, split and size array (#1314) * fix(module: datepicker): incorrect in RTL mode (#1300) * fix(module: dropdown): default PlacementType is incorrect in RTL mode * fix(module: datepicker): not correct in RTL mode * fix(module: datepicker): styles is incorrect when switch to LTR from RTL Co-authored-by: James Yeung <shunjiey@hotmail.com> * docs: fix default css link (#1318) * fix(module: table): bring PR1208 to PR1303 merge master into feature (#1326) * feat(module: pagination): add TotalBoundaryShowSizeChanger parameter (#1325) * feat(module: pagination): add TotalBoundaryShowSizeChanger * docs(module: pagination): update API Co-authored-by: James Yeung <shunjiey@hotmail.com> * docs: dynamic primary color changing (#1332) * feat(docs): change color dynamically * docs: dynamic primary color changing * fix file path * delete the script * feat(module: table): add build-in filters (#1267) * tablefilter-moreoptions * tablefilter-moreoptions * tablefilter-moreoptions * tablefilter-moreoptions * tablefilter-moreoptions * fix demo and docs Co-authored-by: James Yeung <shunjiey@hotmail.com> * fix(module: tooltip): wrong popup direction (#1348) * fix merge conflicts Co-authored-by: Andrzej Bakun <andrzej@neelyc.com.cy> Co-authored-by: LingDev <lingjing0921@live.com> Co-authored-by: Zonciu Liang <zonciu@zonciu.com> Co-authored-by: TimChen <TimChen44@users.noreply.github.com> Co-authored-by: unsung189 <52531536+unsung189@users.noreply.github.com> Co-authored-by: zxyao <zxyao145@gmail.com> Co-authored-by: Patrick <42546986+MutatePat@users.noreply.github.com> Co-authored-by: anranruye <54608128+anranruye@users.noreply.github.com> Co-authored-by: Patryk Grzelak <pgrzelak@mutate.app> Co-authored-by: 笨木头 <musicvs@163.com> Co-authored-by: Magehernan <magehernan@gmail.com> Co-authored-by: ldsenow <ldsenow@gmail.com> Co-authored-by: Leishi <lluo@octet.com> Co-authored-by: YMohd <31305504+YMohd@users.noreply.github.com>
843 lines
29 KiB
C#
843 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using AntDesign.core.Extensions;
|
|
using AntDesign.Core.Helpers;
|
|
using AntDesign.JsInterop;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
|
|
namespace AntDesign
|
|
{
|
|
public partial class Slider<TValue> : AntInputComponentBase<TValue>
|
|
{
|
|
private const string PreFixCls = "ant-slider";
|
|
private Element _sliderDom;
|
|
private Element _leftHandleDom;
|
|
private Element _rightHandleDom;
|
|
private ElementReference _leftHandle;
|
|
private ElementReference _rightHandle;
|
|
private string _leftHandleStyle = "left: 0%; right: auto; transform: translateX(-50%);";
|
|
private string _rightHandleStyle = "left: 0%; right: auto; transform: translateX(-50%);";
|
|
private string _trackStyle = "left: 0%; width: 0%; right: auto;";
|
|
private bool _mouseDown;
|
|
private bool _mouseMove;
|
|
private bool _right = true;
|
|
private bool _initialized = false;
|
|
private double _initialLeftValue;
|
|
private double _initialRightValue;
|
|
private Tooltip _toolTipRight;
|
|
private Tooltip _toolTipLeft;
|
|
|
|
private string RightHandleStyleFormat
|
|
{
|
|
get
|
|
{
|
|
if (Reverse)
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "bottom: auto; top: {0}; transform: translateY(-50%);";
|
|
}
|
|
else
|
|
{
|
|
return "right: {0}; left: auto; transform: translateX(50%);";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "top: auto; bottom: {0}; transform: translateY(50%);";
|
|
}
|
|
else
|
|
{
|
|
return "left: {0}; right: auto; transform: translateX(-50%);";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private string LeftHandleStyleFormat
|
|
{
|
|
get
|
|
{
|
|
if (Reverse)
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "bottom: auto; top: {0}; transform: translateY(-50%);";
|
|
}
|
|
else
|
|
{
|
|
return "right: {0}; left: auto; transform: translateX(50%);";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "top: auto; bottom: {0}; transform: translateY(50%);";
|
|
}
|
|
else
|
|
{
|
|
return "left: {0}; right: auto; transform: translateX(-50%);";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private string TrackStyleFormat
|
|
{
|
|
get
|
|
{
|
|
if (Reverse)
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "bottom: auto; height: {1}; top: {0};";
|
|
}
|
|
else
|
|
{
|
|
return "right: {0}; width: {1}; left: auto;";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Vertical)
|
|
{
|
|
return "top: auto; height: {1}; bottom: {0};";
|
|
}
|
|
else
|
|
{
|
|
return "left: {0}; width: {1}; right: auto;";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Inject]
|
|
private DomEventService DomEventService { get; set; }
|
|
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// The default value of slider. When <see cref="Range"/> is false, use number, otherwise, use [number, number]
|
|
/// </summary>
|
|
[Parameter]
|
|
public TValue DefaultValue { get; set; }
|
|
|
|
/// <summary>
|
|
/// If true, the slider will not be interactable
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Disabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the thumb can drag over tick only
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Dots { get; set; }
|
|
|
|
/// <summary>
|
|
/// Make effect when <see cref="Marks"/> not null, true means containment and false means coordinative
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Included { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Tick mark of Slider, type of key must be number, and must in closed interval [min, max], each mark can declare its own style
|
|
/// </summary>
|
|
[Parameter]
|
|
public SliderMark[] Marks { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum value the slider can slide to
|
|
/// </summary>
|
|
[Parameter]
|
|
public double Max { get; set; } = 100;
|
|
|
|
/// <summary>
|
|
/// The minimum value the slider can slide to
|
|
/// </summary>
|
|
[Parameter]
|
|
public double Min { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// dual thumb mode
|
|
/// </summary>
|
|
//[Parameter]
|
|
private bool? _range;
|
|
|
|
public bool Range
|
|
{
|
|
get
|
|
{
|
|
if (_range == null)
|
|
{
|
|
Type type = typeof(TValue);
|
|
Type doubleType = typeof(double);
|
|
Type tupleDoubleType = typeof((double, double));
|
|
if (type == doubleType)
|
|
{
|
|
_range = false;
|
|
}
|
|
else if (type == tupleDoubleType)
|
|
{
|
|
_range = true;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentOutOfRangeException($"Type argument of Slider should be one of {doubleType}, {tupleDoubleType}");
|
|
}
|
|
}
|
|
return _range.Value;
|
|
}
|
|
//private set { _range = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// reverse the component
|
|
/// </summary>
|
|
private bool _reverse;
|
|
|
|
[Parameter]
|
|
public bool Reverse
|
|
{
|
|
get { return _reverse; }
|
|
set
|
|
{
|
|
if (_reverse != value)
|
|
{
|
|
_reverse = value;
|
|
SetStyle();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The granularity the slider can step through values. Must greater than 0, and be divided by (<see cref="Max"/> - <see cref="Min"/>) . When <see cref="Marks"/> no null, <see cref="Step"/> can be null.
|
|
/// </summary>
|
|
private double? _step = 1;
|
|
|
|
private int _precision;
|
|
|
|
[Parameter]
|
|
public double? Step
|
|
{
|
|
get { return _step; }
|
|
set
|
|
{
|
|
_step = value;
|
|
//no need to evaluate if no tooltip
|
|
if (_step != null && _isTipFormatterDefault)
|
|
{
|
|
char separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
|
|
string[] number = _step.ToString().Split(separator);
|
|
if (number.Length > 1)
|
|
{
|
|
_precision = number[1].Length;
|
|
_tipFormatter = (d) => string.Format(CultureInfo.CurrentCulture, "{0:N02}", Math.Round(d, _precision));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private double _leftValue = double.MinValue;
|
|
|
|
private double LeftValue
|
|
{
|
|
get => _leftValue;
|
|
set
|
|
{
|
|
double candidate = Clamp(value, Min, Max);
|
|
if (_leftValue != candidate)
|
|
{
|
|
_leftValue = candidate;
|
|
SetStyle();
|
|
(double, double) typedValue = DataConvertionExtensions.Convert<TValue, (double, double)>(CurrentValue);
|
|
if (value != typedValue.Item1)
|
|
CurrentValue = DataConvertionExtensions.Convert<(double, double), TValue>((_leftValue, RightValue));
|
|
if (_toolTipLeft != null)
|
|
_toolTipLeft.ChildElementMoved();
|
|
}
|
|
}
|
|
}
|
|
|
|
private double _rightValue = double.MaxValue;
|
|
|
|
// the default non-range value
|
|
private double RightValue
|
|
{
|
|
get => _rightValue;
|
|
set
|
|
{
|
|
double candidate;
|
|
if (Range)
|
|
{
|
|
candidate = Clamp(value, LeftValue, Max);
|
|
}
|
|
else
|
|
{
|
|
candidate = Clamp(value, Min, Max);
|
|
}
|
|
|
|
if (_rightValue != candidate)
|
|
{
|
|
_rightValue = candidate;
|
|
SetStyle();
|
|
if (Range)
|
|
{
|
|
//CurrentValue = TupleToGeneric((LeftValue, _rightValue));
|
|
(double, double) typedValue = DataConvertionExtensions.Convert<TValue, (double, double)>(CurrentValue);
|
|
if (value != typedValue.Item2)
|
|
CurrentValue = DataConvertionExtensions.Convert<(double, double), TValue>((LeftValue, _rightValue));
|
|
}
|
|
else
|
|
{
|
|
double typedValue = DataConvertionExtensions.Convert<TValue, double>(CurrentValue);
|
|
if (value != typedValue)
|
|
//CurrentValue = DoubleToGeneric(_rightValue);
|
|
CurrentValue = DataConvertionExtensions.Convert<double, TValue>(_rightValue);
|
|
}
|
|
if (_toolTipRight != null)
|
|
_toolTipRight.ChildElementMoved();
|
|
}
|
|
}
|
|
}
|
|
|
|
private double Clamp(
|
|
double value, double inclusiveMinimum, double inclusiveMaximum)
|
|
{
|
|
if (value < inclusiveMinimum)
|
|
{
|
|
value = inclusiveMinimum;
|
|
}
|
|
if (value > inclusiveMaximum)
|
|
{
|
|
value = inclusiveMaximum;
|
|
}
|
|
return GetNearestStep(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// If true, the slider will be vertical.
|
|
/// </summary>
|
|
[Parameter]
|
|
public bool Vertical { get; set; }
|
|
|
|
/// <summary>
|
|
/// Fire when onmouseup is fired.
|
|
/// </summary>
|
|
[Parameter]
|
|
public Action<TValue> OnAfterChange { get; set; } //use Action here intead of EventCallback, otherwise VS will not complie when user add a delegate
|
|
|
|
/// <summary>
|
|
/// Callback function that is fired when the user changes the slider's value.
|
|
/// </summary>
|
|
[Parameter]
|
|
public Action<TValue> OnChange { get; set; }
|
|
|
|
[Parameter]
|
|
public bool HasTooltip { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Slider will pass its value to tipFormatter, and display its value in Tooltip
|
|
/// </summary>
|
|
private bool _isTipFormatterDefault = true;
|
|
|
|
private Func<double, string> _tipFormatter = (d) => d.ToString(LocaleProvider.CurrentLocale.CurrentCulture);
|
|
|
|
[Parameter]
|
|
public Func<double, string> TipFormatter
|
|
{
|
|
get { return _tipFormatter; }
|
|
set
|
|
{
|
|
_tipFormatter = value;
|
|
_isTipFormatterDefault = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set Tooltip display position. Ref Tooltip
|
|
/// </summary>
|
|
[Parameter]
|
|
public PlacementType TooltipPlacement { get; set; }
|
|
|
|
/// <summary>
|
|
/// If true, Tooltip will show always, or it will not show anyway, even if dragging or hovering.
|
|
/// </summary>
|
|
private bool _tooltipVisible;
|
|
|
|
private bool _tooltipRightVisible;
|
|
private bool _tooltipLeftVisible;
|
|
|
|
[Parameter]
|
|
public bool TooltipVisible
|
|
{
|
|
get { return _tooltipVisible; }
|
|
set
|
|
{
|
|
if (_tooltipVisible != value)
|
|
{
|
|
_tooltipVisible = value;
|
|
//ensure parameter loading is not happening because values are changing during mouse moving
|
|
//otherwise the tooltip will be vanishing when mouse moves out of the edge
|
|
if (!_mouseDown)
|
|
{
|
|
_tooltipRightVisible = _tooltipVisible;
|
|
_tooltipLeftVisible = _tooltipVisible;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The DOM container of the Tooltip, the default behavior is to create a div element in body.
|
|
/// </summary>
|
|
[Parameter]
|
|
public object GetTooltipPopupContainer { get; set; }
|
|
|
|
#endregion Parameters
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
base.OnInitialized();
|
|
}
|
|
|
|
public async override Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
await base.SetParametersAsync(parameters);
|
|
|
|
var dict = parameters.ToDictionary();
|
|
if (!_initialized)
|
|
{
|
|
if (!dict.ContainsKey(nameof(Value)))
|
|
{
|
|
TValue defaultValue;
|
|
if (Range)
|
|
{
|
|
//if (typeof(T) == typeof((int, int)))
|
|
//{
|
|
// defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<(int, int), T>((0, 0)));
|
|
//}
|
|
//else
|
|
//{
|
|
defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<(double, double), TValue>((0, 0)));
|
|
//}
|
|
LeftValue = DataConvertionExtensions.Convert<TValue, (double, double)>(defaultValue).Item1;
|
|
RightValue = DataConvertionExtensions.Convert<TValue, (double, double)>(defaultValue).Item2;
|
|
}
|
|
else
|
|
{
|
|
//if (typeof(T) == typeof(int))
|
|
//{
|
|
// defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<int, T>(0));
|
|
//}
|
|
//else
|
|
//{
|
|
defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<double, TValue>(0));
|
|
//}
|
|
RightValue = DataConvertionExtensions.Convert<TValue, double>(defaultValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Range)
|
|
{
|
|
LeftValue = DataConvertionExtensions.Convert<TValue, (double, double)>(CurrentValue).Item1;
|
|
RightValue = DataConvertionExtensions.Convert<TValue, (double, double)>(CurrentValue).Item2;
|
|
}
|
|
else
|
|
{
|
|
RightValue = DataConvertionExtensions.Convert<TValue, double>(CurrentValue);
|
|
}
|
|
}
|
|
if (!dict.ContainsKey(nameof(TooltipPlacement)))
|
|
{
|
|
if (Vertical)
|
|
TooltipPlacement = PlacementType.Right;
|
|
else
|
|
TooltipPlacement = PlacementType.Top;
|
|
}
|
|
}
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
base.OnParametersSet();
|
|
|
|
ValidateParameter();
|
|
|
|
ClassMapper.Clear()
|
|
.Add(PreFixCls)
|
|
.If($"{PreFixCls}-disabled", () => Disabled)
|
|
.If($"{PreFixCls}-vertical", () => Vertical)
|
|
.If($"{PreFixCls}-with-marks", () => Marks != null)
|
|
.If($"{PreFixCls}-rtl", () => RTL);
|
|
}
|
|
|
|
protected override void OnAfterRender(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
DomEventService.AddEventListener("window", "mousemove", OnMouseMove, false);
|
|
DomEventService.AddEventListener("window", "mouseup", OnMouseUp, false);
|
|
}
|
|
|
|
base.OnAfterRender(firstRender);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
DomEventService.RemoveEventListerner<JsonElement>("window", "mousemove", OnMouseMove);
|
|
DomEventService.RemoveEventListerner<JsonElement>("window", "mouseup", OnMouseUp);
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (!firstRender)
|
|
{
|
|
if (_toolTipRight != null && HasTooltip)
|
|
{
|
|
_rightHandle = _toolTipRight.Ref;
|
|
await _toolTipRight.ChildElementMoved();
|
|
if (_toolTipLeft != null)
|
|
{
|
|
_leftHandle = _toolTipLeft.Ref;
|
|
await _toolTipLeft.ChildElementMoved();
|
|
}
|
|
}
|
|
}
|
|
await base.OnAfterRenderAsync(firstRender);
|
|
}
|
|
|
|
private void ValidateParameter()
|
|
{
|
|
if (Step == null && Marks == null)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(Step), $"{nameof(Step)} can only be null when {nameof(Marks)} is not null.");
|
|
}
|
|
|
|
if (Step <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(Step), "Must greater than 0.");
|
|
}
|
|
|
|
if (Step != null && (Max - Min) / Step % 1 != 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(Step), $"Must be divided by ({Max} - {Min}).");
|
|
}
|
|
}
|
|
|
|
private async void OnMouseDown(MouseEventArgs args)
|
|
{
|
|
//// _sliderDom = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _slider);
|
|
//_sliderDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, _slider);
|
|
//decimal x = (decimal)args.ClientX;
|
|
//decimal y = (decimal)args.ClientY;
|
|
|
|
//_mouseDown = !Disabled
|
|
// && _sliderDom.clientLeft <= x && x <= _sliderDom.clientLeft + _sliderDom.clientWidth
|
|
// && _sliderDom.clientTop <= y && y <= _sliderDom.clientTop + _sliderDom.clientHeight;
|
|
|
|
_mouseDown = !Disabled;
|
|
}
|
|
|
|
private double _trackedClientX;
|
|
private double _trackedClientY;
|
|
|
|
private void OnMouseDownEdge(MouseEventArgs args, bool right)
|
|
{
|
|
_right = right;
|
|
_initialLeftValue = _leftValue;
|
|
_initialRightValue = _rightValue;
|
|
_trackedClientX = args.ClientX;
|
|
_trackedClientY = args.ClientY;
|
|
if (_toolTipRight != null)
|
|
{
|
|
if (_right)
|
|
{
|
|
_tooltipRightVisible = true;
|
|
}
|
|
else
|
|
{
|
|
_tooltipLeftVisible = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsMoveInEdgeBoundary(JsonElement jsonElement)
|
|
{
|
|
double clientX = jsonElement.GetProperty("clientX").GetDouble();
|
|
double clientY = jsonElement.GetProperty("clientY").GetDouble();
|
|
|
|
return (clientX == _trackedClientX && clientY == _trackedClientY);
|
|
}
|
|
|
|
private async void OnMouseMove(JsonElement jsonElement)
|
|
{
|
|
if (_mouseDown)
|
|
{
|
|
_trackedClientX = jsonElement.GetProperty("clientX").GetDouble();
|
|
_trackedClientY = jsonElement.GetProperty("clientY").GetDouble();
|
|
_mouseMove = true;
|
|
await CalculateValueAsync(Vertical ? jsonElement.GetProperty("pageY").GetDouble() : jsonElement.GetProperty("pageX").GetDouble());
|
|
|
|
OnChange?.Invoke(CurrentValue);
|
|
}
|
|
}
|
|
|
|
private async void OnMouseUp(JsonElement jsonElement)
|
|
{
|
|
bool isMoveInEdgeBoundary = IsMoveInEdgeBoundary(jsonElement);
|
|
if (_mouseDown)
|
|
{
|
|
_mouseDown = false;
|
|
if (!isMoveInEdgeBoundary)
|
|
{
|
|
await CalculateValueAsync(Vertical ? jsonElement.GetProperty("pageY").GetDouble() : jsonElement.GetProperty("pageX").GetDouble());
|
|
OnAfterChange?.Invoke(CurrentValue);
|
|
}
|
|
}
|
|
if (_toolTipRight != null)
|
|
{
|
|
if (_tooltipRightVisible != TooltipVisible)
|
|
{
|
|
_tooltipRightVisible = TooltipVisible;
|
|
_toolTipRight.SetVisible(TooltipVisible);
|
|
}
|
|
|
|
if (_tooltipLeftVisible != TooltipVisible)
|
|
{
|
|
_tooltipLeftVisible = TooltipVisible;
|
|
_toolTipLeft.SetVisible(TooltipVisible);
|
|
}
|
|
}
|
|
|
|
_initialLeftValue = _leftValue;
|
|
_initialRightValue = _rightValue;
|
|
}
|
|
|
|
private async Task CalculateValueAsync(double clickClient)
|
|
{
|
|
_sliderDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, Ref);
|
|
double sliderOffset = (double)(Vertical ? _sliderDom.absoluteTop : _sliderDom.absoluteLeft);
|
|
double sliderLength = (double)(Vertical ? _sliderDom.clientHeight : _sliderDom.clientWidth);
|
|
double handleNewPosition;
|
|
if (_right)
|
|
{
|
|
if (_rightHandleDom == null)
|
|
{
|
|
_rightHandleDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, _rightHandle);
|
|
}
|
|
if (Reverse)
|
|
{
|
|
if (Vertical)
|
|
{
|
|
handleNewPosition = clickClient - sliderOffset;
|
|
}
|
|
else
|
|
{
|
|
handleNewPosition = sliderLength - (clickClient - sliderOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Vertical)
|
|
{
|
|
handleNewPosition = sliderOffset + sliderLength - clickClient;
|
|
}
|
|
else
|
|
{
|
|
handleNewPosition = clickClient - sliderOffset;
|
|
}
|
|
}
|
|
|
|
double rightV = Max * handleNewPosition / sliderLength;
|
|
if (rightV < LeftValue)
|
|
{
|
|
_right = false;
|
|
if (_mouseDown)
|
|
RightValue = _initialLeftValue;
|
|
LeftValue = rightV;
|
|
await FocusAsync(_leftHandle);
|
|
}
|
|
else
|
|
{
|
|
RightValue = rightV;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_leftHandleDom == null)
|
|
{
|
|
_leftHandleDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, _leftHandle);
|
|
}
|
|
if (_rightHandleDom == null)
|
|
{
|
|
_rightHandleDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, _rightHandle);
|
|
}
|
|
if (Reverse)
|
|
{
|
|
if (Vertical)
|
|
{
|
|
handleNewPosition = clickClient - sliderOffset;
|
|
}
|
|
else
|
|
{
|
|
handleNewPosition = sliderLength - (clickClient - sliderOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Vertical)
|
|
{
|
|
handleNewPosition = sliderOffset + sliderLength - clickClient;
|
|
}
|
|
else
|
|
{
|
|
handleNewPosition = clickClient - sliderOffset;
|
|
}
|
|
}
|
|
|
|
double leftV = Max * handleNewPosition / sliderLength;
|
|
if (leftV > RightValue)
|
|
{
|
|
_right = true;
|
|
if (_mouseDown)
|
|
LeftValue = _initialRightValue;
|
|
RightValue = leftV;
|
|
await FocusAsync(_rightHandle);
|
|
}
|
|
else
|
|
{
|
|
LeftValue = leftV;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetStyle()
|
|
{
|
|
_rightHandleStyle = string.Format(CultureInfo.CurrentCulture, RightHandleStyleFormat, Formatter.ToPercentWithoutBlank(RightValue / Max));
|
|
if (Range)
|
|
{
|
|
_trackStyle = string.Format(CultureInfo.CurrentCulture, TrackStyleFormat, Formatter.ToPercentWithoutBlank(LeftValue / Max), Formatter.ToPercentWithoutBlank((RightValue - LeftValue) / Max));
|
|
_leftHandleStyle = string.Format(CultureInfo.CurrentCulture, LeftHandleStyleFormat, Formatter.ToPercentWithoutBlank(LeftValue / Max));
|
|
}
|
|
else
|
|
{
|
|
_trackStyle = string.Format(CultureInfo.CurrentCulture, TrackStyleFormat, "0%", Formatter.ToPercentWithoutBlank(RightValue / Max));
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private string SetMarkPosition(double key)
|
|
{
|
|
return Formatter.ToPercentWithoutBlank(key / Max);
|
|
}
|
|
|
|
private string IsActiveMark(double key)
|
|
{
|
|
bool active = (Range && key >= LeftValue && key <= RightValue)
|
|
|| (!Range && key <= RightValue);
|
|
|
|
return active ? "ant-slider-dot-active" : string.Empty;
|
|
}
|
|
|
|
private double GetNearestStep(double value)
|
|
{
|
|
if (Step.HasValue && (Marks == null || Marks.Length == 0))
|
|
{
|
|
return Math.Round(value / Step.Value, 0) * Step.Value;
|
|
}
|
|
else if (Step.HasValue)
|
|
{
|
|
return new double[2] { Math.Round(value / Step.Value) * Step.Value, Math.Round(value / Step.Value + 1) * Step.Value }.Union(Marks.Select(m => m.Key)).OrderBy(v => Math.Abs(v - value)).First();
|
|
}
|
|
else if (Marks.Length == 0)
|
|
{
|
|
return Min;
|
|
}
|
|
else
|
|
{
|
|
return Marks.Select(m => m.Key).OrderBy(v => Math.Abs(v - value)).First();
|
|
}
|
|
}
|
|
|
|
protected override void OnValueChange(TValue value)
|
|
{
|
|
base.OnValueChange(value);
|
|
|
|
if (Range)
|
|
{
|
|
if (IsLeftAndRightChanged(value))
|
|
{
|
|
_leftValue = double.MinValue;
|
|
_rightValue = double.MaxValue;
|
|
}
|
|
LeftValue = DataConvertionExtensions.Convert<TValue, (double, double)>(value).Item1;
|
|
RightValue = DataConvertionExtensions.Convert<TValue, (double, double)>(value).Item2;
|
|
}
|
|
else
|
|
{
|
|
RightValue = DataConvertionExtensions.Convert<TValue, double>(value);
|
|
}
|
|
}
|
|
|
|
private bool IsLeftAndRightChanged(TValue value)
|
|
{
|
|
(double, double) typedValue = DataConvertionExtensions.Convert<TValue, (double, double)>(value);
|
|
return (typedValue.Item1 != LeftValue) && (typedValue.Item2 != RightValue);
|
|
}
|
|
|
|
private TValue _value;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value of the input. This should be used with two-way binding.
|
|
/// </summary>
|
|
/// <example>
|
|
/// @bind-Value="model.PropertyName"
|
|
/// </example>
|
|
[Parameter]
|
|
public sealed override TValue Value
|
|
{
|
|
get { return _value; }
|
|
set
|
|
{
|
|
TValue orderedValue = SortValue(value);
|
|
var hasChanged = !EqualityComparer<TValue>.Default.Equals(orderedValue, Value);
|
|
if (hasChanged)
|
|
{
|
|
_value = orderedValue;
|
|
OnValueChange(orderedValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
private TValue SortValue(TValue value)
|
|
{
|
|
TValue orderedValue = value;
|
|
if (Range)
|
|
{
|
|
//sort if needed
|
|
(double, double) typedValue = DataConvertionExtensions.Convert<TValue, (double, double)>(value);
|
|
if (typedValue.Item1 > typedValue.Item2)
|
|
{
|
|
orderedValue = DataConvertionExtensions.Convert<(double, double), TValue>((typedValue.Item2, typedValue.Item1));
|
|
}
|
|
}
|
|
return orderedValue;
|
|
}
|
|
}
|
|
}
|