Skip to content

Commit

Permalink
Add Sliders for commissionFields, so they can be changed
Browse files Browse the repository at this point in the history
  • Loading branch information
Hjort committed Oct 6, 2023
1 parent 7adb1cb commit ac09969
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 30 deletions.
6 changes: 6 additions & 0 deletions packages/browser-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Added

- When creating/updating a baker, the commission rates can now be changed, if the ranges are not singletons.

## 1.1.8

### Added
Expand Down
1 change: 1 addition & 0 deletions packages/browser-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"leb128": "^0.0.5",
"lodash.debounce": "^4.0.8",
"lodash.groupby": "^4.6.0",
"rc-slider": "9",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-hook-form": "^7.30.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
.account-transaction-flow {
padding: rem(10px) rem(15px);
padding: rem(10px) 0 rem(10px) rem(16px);
display: flex;
flex-direction: column;
width: 100%;
justify-content: space-between;
background-color: $color-bg;
min-height: 100%;
height: 100%;
white-space: break-spaces;
scrollbar-gutter: stable;
overflow: overlay;

&__header {
position: relative;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CommonFieldProps, RequiredUncontrolledFieldProps } from '@popup/shared/Form/common/types';
import { makeUncontrolled } from '@popup/shared/Form/common/utils';
import { CommonFieldProps, RequiredControlledFieldProps } from '@popup/shared/Form/common/types';
import { makeControlled } from '@popup/shared/Form/common/utils';
import Slider from '@popup/shared/Form/Slider';
import clsx from 'clsx';
import React, { forwardRef, InputHTMLAttributes } from 'react';
import React, { InputHTMLAttributes } from 'react';
import { PropsOf } from 'wallet-common-helpers';

interface CommissionFieldProps {
label: string;
Expand All @@ -10,31 +12,62 @@ interface CommissionFieldProps {
min: number;
/** Decimal */
max: number;
/** Decimal */
existing?: number;
}

const commonSliderProps: Pick<PropsOf<typeof Slider>, 'step' | 'unit' | 'className'> = {
step: 0.001,
unit: '%',
className: 'm-b-10',
};

type Props = Pick<InputHTMLAttributes<HTMLInputElement>, 'type' | 'className' | 'autoFocus'> &
RequiredUncontrolledFieldProps<HTMLInputElement> &
RequiredControlledFieldProps &
CommonFieldProps &
CommissionFieldProps;

// TODO Implement sliders for commissions
export const CommissionInput = forwardRef<HTMLInputElement, Props>(
({ error, className, type, name, min, max, label, note, valid, ...props }, ref) => {
// Default to max percentage
const value = max * 100;
export function CommissionInput({
error,
className,
type,
name,
min,
max,
label,
note,
valid,
onChange,
onBlur,
value,
...props
}: Props) {
const minPercentage = min * 100;
const maxPercentage = max * 100;

if (min === max) {
return (
<>
<input type="hidden" name={name} ref={ref} value={value} {...props} />
<input type="hidden" name={name} value={minPercentage} {...props} />
<div className={clsx('baking__commissionField-slider', className)}>
{label && <div className="baking__commissionField-label">{label}</div>}
<div className="baking__commissionField-value">{value}%</div>
<div className="baking__commissionField-value">{minPercentage}%</div>
</div>
</>
);
}
);

const CommissionsField = makeUncontrolled(CommissionInput);
return (
<Slider
value={value}
label={label}
name={name}
min={minPercentage}
max={maxPercentage}
onChange={onChange}
onBlur={onBlur}
{...commonSliderProps}
/>
);
}

const CommissionsField = makeControlled(CommissionInput);
export default CommissionsField;
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,30 @@ export default function CommissionsPage({ initial, onNext }: CommissionsProps) {
{(f) => (
<>
<div>
<div className="m-t-0">{t('commission.description')}</div>
<div className="m-t-0 m-b-10">{t('commission.description')}</div>
<CommissionField
label={tShared('baking.transactionCommission')}
name="transactionCommission"
min={chainParameters.transactionCommissionRange.min}
max={chainParameters.transactionCommissionRange.max}
register={f.register}
control={f.control}
/>
<CommissionField
label={tShared('baking.bakingCommission')}
name="bakingCommission"
min={chainParameters.bakingCommissionRange.min}
register={f.register}
max={chainParameters.bakingCommissionRange.max}
control={f.control}
/>
<CommissionField
label={tShared('baking.finalizationCommission')}
name="finalizationCommission"
min={chainParameters.finalizationCommissionRange.min}
register={f.register}
max={chainParameters.finalizationCommissionRange.max}
control={f.control}
/>
</div>
<Submit className="m-t-20" width="wide">
<Submit className="m-t-10" width="wide">
{tShared('continue')}
</Submit>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
OpenStatusText,
BakerKeysWithProofs,
} from '@concordium/web-sdk';
import { decimalToRewardFraction } from '@popup/shared/utils/baking-helpers';
import { decimalToRewardFraction, fractionToPercentage } from '@popup/shared/utils/baking-helpers';
import { getConfigureBakerEnergyCost } from '@shared/utils/energy-helpers';
import { not } from '@shared/utils/function-helpers';
import { ccdToMicroCcd, isDefined, isValidCcdString, microCcdToCcd, NotOptional } from 'wallet-common-helpers';
Expand Down Expand Up @@ -51,7 +51,11 @@ export const getExistingBakerValues = (accountInfo: AccountInfo): NotOptional<Co
restake: restakeEarnings,
openForDelegation: openStatusFromText(openStatus),
metadataUrl,
commissionRates,
commissionRates: {
transactionCommission: fractionToPercentage(commissionRates.transactionCommission),
bakingCommission: fractionToPercentage(commissionRates.bakingCommission),
finalizationCommission: fractionToPercentage(commissionRates.finalizationCommission),
},
};
};

Expand Down
68 changes: 68 additions & 0 deletions packages/browser-wallet/src/popup/shared/Form/Form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,71 @@ $handle-scale: scale(1.02);
}
}
}

.form-slider {
display: block;
text-align: center;

&__slider {
height: 30px;
padding: 0;
z-index: 0;
}

.rc-slider-rail {
background-color: transparent;
border: 1px solid $color-grey;
height: 100%;
z-index: 1;

.invalid & {
border-color: $color-error;
border-width: 2px;
}
}

.rc-slider-track {
background-color: $color-cta;
height: 100%;
}

.rc-slider-handle {
top: 50%;
border-color: $color-grey;
margin-top: -7px;
z-index: 2;

&:hover,
&:active {
border-color: $color-cta;
}

.invalid & {
&,
&:hover,
&:active {
border-color: $color-error;
}
}
}

&__grid {
display: grid;
grid-template-columns: 60px auto 60px;
grid-gap: 5px;
align-items: center;
justify-items: center;
}

&__inputWrapper {
grid-column-start: 2;
border-bottom: 1px solid $color-text;
width: 100%;
max-width: 200px;

.invalid & {
color: $color-error;
border-color: $color-error;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ type Props = Pick<InputHTMLAttributes<HTMLInputElement>, 'type' | 'className' |
RequiredControlledFieldProps &
CommonFieldProps & {
fallbackValue?: string;
fallbackOnError?: boolean;
};

export function InlineInput({
className,
type = 'text',
value,
fallbackValue,
fallbackOnError = false,
onChange = noOp,
onBlur = noOp,
error,
Expand All @@ -34,7 +36,7 @@ export function InlineInput({

const handleBlur = useCallback(() => {
onBlur();
if (!value) {
if (!value || (fallbackOnError && error)) {
onChange(fallbackValue);
}
}, [onBlur, value, fallbackValue, onChange]);
Expand Down
102 changes: 102 additions & 0 deletions packages/browser-wallet/src/popup/shared/Form/Slider/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import RcSlider from 'rc-slider';
import clsx from 'clsx';
import { noOp, toFixed, valueNoOp } from 'wallet-common-helpers';
import { CommonFieldProps } from '../common/types';
import { InlineInput } from '../InlineInput';

interface Props extends CommonFieldProps {
min: number;
max: number;
step: number;
unit?: string;
value: number | undefined;
onChange?(value: number | number[] | undefined): void;
onBlur?(): void;
className: string;
name: string;
}

export default function Slider({
min,
max,
step,
label,
unit = '',
onChange = noOp,
onBlur = noOp,
value,
className,
name,
}: Props) {
const [innerValue, setInnerValue] = useState<number | undefined>(value);
const isInvalid = innerValue !== undefined && (Number.isNaN(innerValue) || innerValue > max || innerValue < min);

const allowFractions = step < 1 && step > 0;
const ensureDigits = allowFractions ? step.toString().split('.')[1].length : undefined;
const formatNumber = ensureDigits ? toFixed(ensureDigits) : valueNoOp;

useEffect(() => {
onChange(innerValue);
}, [innerValue, onChange]);

const handleChange = (v: string | undefined) => {
if (v === undefined) {
setInnerValue(0);
return;
}

const parser = Number.isInteger(step) ? parseInt : parseFloat;
setInnerValue(parser(v));
};

const handleBlur = () => {
onBlur();
const parser = Number.isInteger(step) ? parseInt : parseFloat;
setInnerValue(innerValue ? parser(formatNumber(innerValue.toString())) : innerValue);
};

if (min > max) {
throw new Error('Prop "min" must be lower that prop "max"');
}

return (
<label className={clsx('form-slider', isInvalid && 'form-slider__invalid', className)}>
<label className="m-b-5">{label}</label>
<div className="form-slider__grid">
<span>
Min:
<br />
{formatNumber(min.toString())}
{unit}
</span>
<RcSlider
className="form-slider__slider"
value={innerValue}
onChange={(v: number) => setInnerValue(v)}
min={min}
max={max}
step={step}
/>
<span>
Max:
<br />
{formatNumber(max.toString())}
{unit}
</span>
<div className="form-slider__inputWrapper">
<InlineInput
value={innerValue?.toString()}
error={isInvalid ? 'value must be number in range' : undefined}
onChange={handleChange}
onBlur={handleBlur}
fallbackValue={min.toString()}
name={name}
fallbackOnError
/>
{unit}
</div>
</div>
</label>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Slider';
Loading

0 comments on commit ac09969

Please sign in to comment.