Skip to content

Commit e375c82

Browse files
authored
feat: dependent properties and refactor withValidPropValue (#9713)
2 parents 16e8038 + 72593c3 commit e375c82

File tree

11 files changed

+98
-67
lines changed

11 files changed

+98
-67
lines changed

packages/components/src/internal/functional-components/avatar/controller.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { ColorPair } from '../../../schema';
2-
import type { ColorProp, InitialsProp, LabelProp, SrcProp } from '../../props';
32
import { colorProp, initialsProp, labelProp, srcProp } from '../../props';
4-
import { withValidPropValue } from '../../props/helpers/factory';
53
import { BaseController } from '../base-controller';
64
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
75
import type { AvatarApi } from './api';
@@ -23,22 +21,22 @@ export class AvatarController extends BaseController<AvatarApi> implements Contr
2321
}
2422

2523
public watchColor(value?: string | ColorPair): void {
26-
withValidPropValue<ColorProp>(colorProp, value, (v) => {
24+
colorProp.apply(value, (v) => {
2725
this.setProp('color', v);
2826
});
2927
}
3028

3129
public watchLabel(value?: string): void {
32-
withValidPropValue<LabelProp>(labelProp, value, (v) => {
30+
labelProp.apply(value, (v) => {
3331
this.setProp('label', v);
3432
});
35-
withValidPropValue<InitialsProp>(initialsProp, value, (v) => {
33+
initialsProp.apply(value, (v) => {
3634
this.setState('initials', v);
3735
});
3836
}
3937

4038
public watchSrc(value?: string): void {
41-
withValidPropValue<SrcProp>(srcProp, value, (v) => {
39+
srcProp.apply(value, (v) => {
4240
this.setProp('src', v);
4341
});
4442
}

packages/components/src/internal/functional-components/click-button/controller.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import type { LabelProp } from '../../props';
2-
import { labelProp, withValidPropValue } from '../../props';
1+
import { labelProp } from '../../props';
32
import { BaseController } from '../base-controller';
43
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
54
import type { ClickButtonApi } from './api';
@@ -19,7 +18,7 @@ export class ClickButtonController extends BaseController<ClickButtonApi> implem
1918
}
2019

2120
public watchLabel(value?: string): void {
22-
withValidPropValue<LabelProp>(labelProp, value, (v) => {
21+
labelProp.apply(value, (v) => {
2322
this.setProp('label', v);
2423
});
2524
}

packages/components/src/internal/functional-components/image/controller.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { AltProp, LoadingProp, LoadingType, SizesProp, SrcProp, SrcsetProp } from '../../props';
2-
import { altProp, loadingProp, sizesProp, srcProp, srcsetProp, withValidPropValue } from '../../props';
1+
import type { LoadingType } from '../../props';
2+
import { altProp, loadingProp, sizesProp, srcProp, srcsetProp } from '../../props';
33
import { BaseController } from '../base-controller';
44
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
55
import type { ImageApi } from './api';
@@ -25,31 +25,31 @@ export class ImageController extends BaseController<ImageApi> implements Control
2525
}
2626

2727
public watchAlt(value?: string): void {
28-
withValidPropValue<AltProp>(altProp, value, (v) => {
28+
altProp.apply(value, (v) => {
2929
this.setProp('alt', v);
3030
});
3131
}
3232

3333
public watchLoading(value?: LoadingType): void {
34-
withValidPropValue<LoadingProp>(loadingProp, value, (v) => {
34+
loadingProp.apply(value, (v) => {
3535
this.setProp('loading', v);
3636
});
3737
}
3838

3939
public watchSizes(value?: string): void {
40-
withValidPropValue<SizesProp>(sizesProp, value, (v) => {
40+
sizesProp.apply(value, (v) => {
4141
this.setProp('sizes', v);
4242
});
4343
}
4444

4545
public watchSrc(value?: string): void {
46-
withValidPropValue<SrcProp>(srcProp, value, (v) => {
46+
srcProp.apply(value, (v) => {
4747
this.setProp('src', v);
4848
});
4949
}
5050

5151
public watchSrcset(value?: string): void {
52-
withValidPropValue<SrcsetProp>(srcsetProp, value, (v) => {
52+
srcsetProp.apply(value, (v) => {
5353
this.setProp('srcset', v);
5454
});
5555
}

packages/components/src/internal/functional-components/progress/api.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import type { LabelProp, MaxProp, UnitProp, ValueProp, VariantProgressProp } from '../../props';
1+
import type { LabelProp, MaxProp, UnitProp, NumberValueProp, VariantProgressProp } from '../../props';
22
import type { ComponentApi, InternalOf } from '../generic-types';
33

44
export interface ProgressApi extends ComponentApi {
55
Props: {
66
Optional: LabelProp & UnitProp & VariantProgressProp;
7-
Required: MaxProp & ValueProp;
7+
Required: MaxProp & NumberValueProp;
88
};
99
States: InternalOf<UnitProp> &
1010
InternalOf<VariantProgressProp> & {

packages/components/src/internal/functional-components/progress/controller.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import type { LabelProp, MaxProp, UnitProp, ValueProp, VariantProgressProp } from '../../props';
2-
import { labelProp, maxProp, unitProp, valueProp, variantProgressProp, withValidPropValue } from '../../props';
1+
import { clampedNumberValueProp, labelProp, maxProp, unitProp, variantProgressProp } from '../../props';
32
import { BaseController } from '../base-controller';
43
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
54
import type { ProgressApi } from './api';
@@ -31,52 +30,43 @@ export class ProgressController extends BaseController<ProgressApi> implements C
3130
}
3231

3332
public watchLabel(value?: string): void {
34-
withValidPropValue<LabelProp>(labelProp, value, (v) => {
33+
labelProp.apply(value, (v) => {
3534
this.setProp('label', v);
3635
});
3736
}
3837

3938
public watchMax(value?: number): void {
40-
withValidPropValue<MaxProp>(maxProp, value, (v) => {
39+
maxProp.apply(value, (v) => {
4140
this.setProp('max', v);
4241
this.setState('max', v);
4342
});
4443
this.watchValue(this.getProps().value);
4544
}
4645

4746
public watchUnit(value?: string): void {
48-
withValidPropValue<UnitProp>(unitProp, value, (v) => {
47+
unitProp.apply(value, (v) => {
4948
this.setProp('unit', v);
5049
this.setState('unit', v);
5150
});
5251
}
5352

5453
public watchValue(value?: number): void {
55-
withValidPropValue<ValueProp>(valueProp, this.clampValue(value), (v) => {
56-
this.setProp('value', v);
57-
});
54+
clampedNumberValueProp.apply(
55+
value,
56+
(v) => {
57+
this.setProp('value', v);
58+
},
59+
{ min: 0, max: this.getProps().max },
60+
);
5861
}
5962

6063
public watchVariant(value?: string): void {
61-
withValidPropValue<VariantProgressProp>(variantProgressProp, value, (v) => {
64+
variantProgressProp.apply(value, (v) => {
6265
this.setProp('variant', v);
6366
this.setState('variant', v);
6467
});
6568
}
6669

67-
private clampValue(value?: number): number | undefined {
68-
if (typeof value === 'number') {
69-
if (value > this.component.max) {
70-
value = this.component.max;
71-
}
72-
73-
if (value < 0) {
74-
value = 0;
75-
}
76-
}
77-
return value;
78-
}
79-
8070
// a11y: says the value of the component every 5s
8171
private startLiveValueInterval(): void {
8272
this.interval = setInterval(() => {

packages/components/src/internal/functional-components/quote/controller.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import type { HrefProp, LabelProp, QuoteProp, VariantQuoteProp } from '../../props';
2-
import { hrefProp, labelProp, quoteProp, variantQuoteProp, withValidPropValue } from '../../props';
1+
import { hrefProp, labelProp, quoteProp, variantQuoteProp } from '../../props';
32
import { BaseController } from '../base-controller';
43
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
54
import type { QuoteApi } from './api';
@@ -26,25 +25,25 @@ export class QuoteController extends BaseController<QuoteApi> implements Control
2625
}
2726

2827
public watchHref(value?: string): void {
29-
withValidPropValue<HrefProp>(hrefProp, value, (v) => {
28+
hrefProp.apply(value, (v) => {
3029
this.setProp('href', v);
3130
});
3231
}
3332

3433
public watchLabel(value?: string): void {
35-
withValidPropValue<LabelProp>(labelProp, value, (v) => {
34+
labelProp.apply(value, (v) => {
3635
this.setProp('label', v);
3736
});
3837
}
3938

4039
public watchQuote(value?: string): void {
41-
withValidPropValue<QuoteProp>(quoteProp, value, (v) => {
40+
quoteProp.apply(value, (v) => {
4241
this.setProp('quote', v);
4342
});
4443
}
4544

4645
public watchVariant(value?: string): void {
47-
withValidPropValue<VariantQuoteProp>(variantQuoteProp, value, (v) => {
46+
variantQuoteProp.apply(value, (v) => {
4847
this.setProp('variant', v);
4948
});
5049
}

packages/components/src/internal/functional-components/skeleton/controller.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Log } from '../../../schema';
2-
import type { CountProp, LabelProp, NameProp } from '../../props';
3-
import { countProp, labelProp, nameProp, withValidPropValue } from '../../props';
2+
import { countProp, labelProp, nameProp } from '../../props';
43
import { BaseController } from '../base-controller';
54
import { ClickButtonController } from '../click-button/controller';
65
import type { ControllerInterface, ResolvedInputProps } from '../generic-types';
@@ -31,20 +30,20 @@ export class SkeletonController extends BaseController<SkeletonApi> implements C
3130
}
3231

3332
public watchCount(value?: number | string): void {
34-
withValidPropValue<CountProp>(countProp, value, (v) => {
33+
countProp.apply(value, (v) => {
3534
this.setProp('count', v);
3635
this.setState('count', v);
3736
});
3837
}
3938

4039
public watchName(value?: string): void {
41-
withValidPropValue<NameProp>(nameProp, value, (v) => {
40+
nameProp.apply(value, (v) => {
4241
this.setProp('name', v);
4342
});
4443
}
4544

4645
public watchLabel(value?: string): void {
47-
withValidPropValue<LabelProp>(labelProp, value, (v) => {
46+
labelProp.apply(value, (v) => {
4847
this.setState('label', v);
4948
this.clickButtonCtrl.watchLabel(v);
5049
});

packages/components/src/internal/props/helpers/factory.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type InternalPropValue<P extends Prop<string, unknown, unknown>> = NonNul
3131
export type PropDefinition<TInternal> = {
3232
normalize: (value: unknown) => TInternal | never;
3333
validate: (value: TInternal) => boolean;
34+
apply: (value: unknown, callback: (normalized: TInternal) => void) => void;
3435
};
3536

3637
export function createPropDefinition<P extends Prop<string, unknown, unknown>>(
@@ -40,20 +41,41 @@ export function createPropDefinition<P extends Prop<string, unknown, unknown>>(
4041
return {
4142
normalize,
4243
validate,
44+
apply(value, callback) {
45+
try {
46+
const normalized = this.normalize(value);
47+
if (this.validate(normalized)) {
48+
callback(normalized);
49+
}
50+
} catch (e) {
51+
Log.debug(e);
52+
}
53+
},
4354
};
4455
}
4556

46-
export function withValidPropValue<P extends Prop<string, unknown, unknown>>(
47-
propDef: PropDefinition<InternalPropValue<P>>,
48-
value: unknown,
49-
callback: (normalized: InternalPropValue<P>) => void,
50-
): void {
51-
try {
52-
const normalized = propDef.normalize(value);
53-
if (propDef.validate(normalized)) {
54-
callback(normalized);
55-
}
56-
} catch (e) {
57-
Log.debug(e);
58-
}
57+
export type DependentPropDefinition<TInternal, TDeps> = {
58+
normalize: (value: unknown, deps: TDeps) => TInternal | never;
59+
validate: (value: TInternal, deps: TDeps) => boolean;
60+
apply: (value: unknown, callback: (normalized: TInternal) => void, deps: TDeps) => void;
61+
};
62+
63+
export function createDependentPropDefinition<P extends Prop<string, unknown, unknown>, TDeps>(
64+
normalize: (value: unknown, deps: TDeps) => InternalPropValue<P> | never,
65+
validate: (value: InternalPropValue<P>, deps: TDeps) => boolean = () => true,
66+
): DependentPropDefinition<InternalPropValue<P>, TDeps> {
67+
return {
68+
normalize,
69+
validate,
70+
apply(value, callback, deps: TDeps) {
71+
try {
72+
const normalized = this.normalize(value, deps);
73+
if (this.validate(normalized, deps)) {
74+
callback(normalized);
75+
}
76+
} catch (e) {
77+
Log.debug(e);
78+
}
79+
},
80+
};
5981
}

packages/components/src/internal/props/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './sizes';
1414
export * from './src';
1515
export * from './srcset';
1616
export * from './unit';
17-
export * from './value';
17+
export * from './value-number';
18+
export * from './value-number-clamped';
1819
export * from './variant-progress';
1920
export * from './variant-quote';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { SimpleProp } from './helpers/factory';
2+
import { createDependentPropDefinition } from './helpers/factory';
3+
import { normalizeNumber } from './helpers/normalizers';
4+
5+
export type ClampedNumberValueProp = SimpleProp<'value', number>;
6+
7+
export type ClampedNumberValueDeps = {
8+
min: number;
9+
max: number;
10+
};
11+
12+
export const clampedNumberValueProp = createDependentPropDefinition<ClampedNumberValueProp, ClampedNumberValueDeps>(
13+
(value, deps) => {
14+
const normalized = normalizeNumber(value);
15+
if (normalized < deps.min) {
16+
return deps.min;
17+
} else if (normalized > deps.max) {
18+
return deps.max;
19+
}
20+
return normalized;
21+
},
22+
(v) => v >= 0,
23+
);

0 commit comments

Comments
 (0)