Skip to content

Commit

Permalink
feat: add tooltip and withTooltip options
Browse files Browse the repository at this point in the history
  • Loading branch information
victorgarciaesgi committed Dec 9, 2024
1 parent 625ac68 commit f7d3474
Show file tree
Hide file tree
Showing 24 changed files with 583 additions and 111 deletions.
25 changes: 25 additions & 0 deletions docs/src/core-concepts/validation-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,28 @@ Will reset both your validation state and your form state to their initial value

Clears the $externalResults state back to an empty object.



## Specific properties for fields

### `$rules`
- Type: `Record<string, RegleRuleStatus>`

This is reactive tree containing all the declared rules of your field.
To know more about the rule properties check the [rules properties section](/core-concepts/rules-properties)


## Specific properties for nested objects

### `$fields`
- Type: `Record<string, RegleStatu | RegleFieldStatus | RegleCollectionStatus>`

This represents all the children of your object. You can access any nested child at any depth to get the relevant data you need for your form.


## Specific properties for collections


### `$each`

### `$field`
9 changes: 3 additions & 6 deletions packages/core/src/core/createRule/createRule.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type {
InferRegleRule,
RegleRuleInit,
RegleRuleMetadataDefinition,
RegleUniversalParams,
} from '../../types';
import type { InferRegleRule, RegleRuleInit, RegleRuleMetadataDefinition, RegleUniversalParams } from '../../types';
import { defineRuleProcessors } from './defineRuleProcessors';
import { getFunctionParametersLength } from './unwrapRuleParameters';

Expand Down Expand Up @@ -63,12 +58,14 @@ export function createRule<
ruleFactory.validator = staticProcessors.validator;
ruleFactory.message = staticProcessors.message;
ruleFactory.active = staticProcessors.active;
ruleFactory.tooltip = staticProcessors.tooltip;
ruleFactory.type = staticProcessors.type;
ruleFactory.exec = staticProcessors.exec;

ruleFactory._validator = staticProcessors.validator;
ruleFactory._message = staticProcessors.message;
ruleFactory._active = staticProcessors.active;
ruleFactory._tooltip = staticProcessors.tooltip;
ruleFactory._type = definition.type;
ruleFactory._patched = false;
ruleFactory._async = isAsync as TAsync;
Expand Down
31 changes: 19 additions & 12 deletions packages/core/src/core/createRule/defineRuleProcessors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function defineRuleProcessors(
definition: $InternalRegleRuleInit,
...params: any[]
): $InternalRegleRuleDefinition {
const { message, validator, active, type, ...properties } = definition;
const { validator, type } = definition;

const isAsync = type === InternalRuleType.Async || validator.constructor.name === 'AsyncFunction';

Expand All @@ -29,7 +29,6 @@ export function defineRuleProcessors(
return definition.message;
}
},

active(value: any, metadata: $InternalRegleRuleMetadataConsumer) {
if (typeof definition.active === 'function') {
return definition.active(value, {
Expand All @@ -40,6 +39,16 @@ export function defineRuleProcessors(
return definition.active ?? true;
}
},
tooltip(value: any, metadata: $InternalRegleRuleMetadataConsumer) {
if (typeof definition.tooltip === 'function') {
return definition.tooltip(value, {
...metadata,
$params: unwrapRuleParameters(metadata.$params?.length ? metadata.$params : params),
});
} else {
return definition.tooltip ?? [];
}
},
exec(value: any) {
const validator = definition.validator(value, ...unwrapRuleParameters(params));
let rawResult: RegleRuleMetadataDefinition;
Expand Down Expand Up @@ -68,16 +77,14 @@ export function defineRuleProcessors(

const processors = {
...defaultProcessors,
...properties,
...{
_validator: definition.validator as any,
_message: definition.message,
_active: definition.active,
_type: definition.type,
_patched: false,
_async: isAsync,
_params: createReactiveParams<never>(params),
},
_validator: definition.validator as any,
_message: definition.message,
_active: definition.active,
_tooltip: definition.tooltip,
_type: definition.type,
_patched: false,
_async: isAsync,
_params: createReactiveParams<never>(params),
};

return processors;
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/core/useRegle/useErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ export function extractRulesErrors({
}, [])
.concat(field.$dirty ? (field.$externalErrors ?? []) : []);
}

export function extractRulesTooltips({ field }: { field: Pick<$InternalRegleFieldStatus, '$rules'> }): string[] {
return Object.entries(field.$rules ?? {})
.map(([ruleKey, rule]) => rule.$tooltip)
.filter((tooltip): tooltip is string | string[] => !!tooltip)
.reduce<string[]>((acc, value) => {
if (typeof value === 'string') {
return acc?.concat([value]);
} else {
return acc?.concat(value);
}
}, []);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
RegleShortcutDefinition,
} from '../../../types';
import { debounce, isVueSuperiorOrEqualTo3dotFive, resetFieldValue } from '../../../utils';
import { extractRulesErrors } from '../useErrors';
import { extractRulesErrors, extractRulesTooltips } from '../useErrors';
import type { CommonResolverOptions, CommonResolverScopedState } from './common/common-types';
import { createReactiveRuleStatus } from './createReactiveRuleStatus';

Expand Down Expand Up @@ -46,6 +46,7 @@ export function createReactiveFieldStatus({
$clearExternalErrorsOnChange: ComputedRef<boolean | undefined>;
$errors: ComputedRef<string[]>;
$silentErrors: ComputedRef<string[]>;
$tooltips: ComputedRef<string[]>;
$haveAnyAsyncRule: ComputedRef<boolean>;
$ready: ComputedRef<boolean>;
$shortcuts: ToRefs<RegleShortcutDefinition['fields']>;
Expand Down Expand Up @@ -207,6 +208,17 @@ export function createReactiveFieldStatus({
return [];
});

const $tooltips = computed<string[]>(() => {
if ($error.value) {
return extractRulesTooltips({
field: {
$rules: $rules.value,
},
});
}
return [];
});

const $silentErrors = computed<string[]>(() => {
return extractRulesErrors({
field: {
Expand Down Expand Up @@ -261,7 +273,7 @@ export function createReactiveFieldStatus({

const $haveAnyAsyncRule = computed(() => {
return Object.entries($rules.value).some(([key, ruleResult]) => {
return ruleResult._haveAsync;
return ruleResult.$haveAsync;
});
});

Expand All @@ -288,6 +300,7 @@ export function createReactiveFieldStatus({
$ready,
$silentErrors,
$anyDirty,
$tooltips,
$name,
})
);
Expand Down Expand Up @@ -327,6 +340,7 @@ export function createReactiveFieldStatus({
$haveAnyAsyncRule,
$shortcuts,
$validating,
$tooltips,
} satisfies ScopeReturnState;
})!;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function createReactiveRuleStatus({
type ScopeState = {
$active: ComputedRef<boolean>;
$message: ComputedRef<string | string[]>;
$tooltip: ComputedRef<string | string[]>;
$type: ComputedRef<string>;
$validator: ComputedRef<
RegleRuleDefinitionProcessor<any, any, RegleRuleMetadataDefinition | Promise<RegleRuleMetadataDefinition>>
Expand All @@ -51,7 +52,7 @@ export function createReactiveRuleStatus({

let $unwatchState: WatchStopHandle;

const _haveAsync = ref(false);
const $haveAsync = ref(false);

const { $pending, $valid, $metadata, $validating } = storage.trySetRuleStatusRef(`${path}.${ruleKey}`);

Expand All @@ -75,26 +76,31 @@ export function createReactiveRuleStatus({
}
});

const $message = computed<string | string[]>(() => {
let message: string | string[] = '';
const customMessageRule = customMessages ? customMessages[ruleKey]?.message : undefined;
function computeRuleProcessor(key: 'message' | 'tooltip'): string | string[] {
let result: string | string[] = '';
const customMessageRule = customMessages ? customMessages[ruleKey]?.[key] : undefined;

if (customMessageRule) {
if (typeof customMessageRule === 'function') {
message = customMessageRule(state.value, $defaultMetadata.value);
result = customMessageRule(state.value, $defaultMetadata.value);
} else {
message = customMessageRule;
result = customMessageRule;
}
}
if (isFormRuleDefinition(rule)) {
if (!(customMessageRule && !rule.value._patched)) {
if (typeof rule.value.message === 'function') {
message = rule.value.message(state.value, $defaultMetadata.value);
if (typeof rule.value[key] === 'function') {
result = rule.value[key](state.value, $defaultMetadata.value);
} else {
message = rule.value.message;
result = rule.value[key] ?? '';
}
}
}
return result;
}

const $message = computed<string | string[]>(() => {
let message = computeRuleProcessor('message');

if (isEmpty(message)) {
message = 'Error';
Expand All @@ -104,6 +110,10 @@ export function createReactiveRuleStatus({
return message;
});

const $tooltip = computed<string | string[]>(() => {
return computeRuleProcessor('tooltip');
});

const $type = computed(() => {
if (isFormRuleDefinition(rule) && rule.value.type) {
return rule.value.type;
Expand Down Expand Up @@ -137,6 +147,7 @@ export function createReactiveRuleStatus({
$validator,
$params,
$path,
$tooltip,
} satisfies ScopeState;
})!;

Expand Down Expand Up @@ -245,7 +256,7 @@ export function createReactiveRuleStatus({
$pending,
$valid,
$metadata,
_haveAsync,
$haveAsync,
$validating,
$validate,
$unwatch,
Expand Down
10 changes: 4 additions & 6 deletions packages/core/src/types/rules/rule.definition.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@ export interface RegleRuleDefinition<
TParams,
TAsync extends false ? TMetaData : Promise<TMetaData>
>;
message: RegleRuleDefinitionWithMetadataProcessor<
TFilteredValue,
PossibleRegleRuleMetadataConsumer,
string | string[]
>;
active: RegleRuleDefinitionWithMetadataProcessor<TFilteredValue, PossibleRegleRuleMetadataConsumer, boolean>;
message: (value: Maybe<TFilteredValue>, metadata: PossibleRegleRuleMetadataConsumer) => string | string[];
active: (value: Maybe<TFilteredValue>, metadata: PossibleRegleRuleMetadataConsumer) => boolean;
tooltip: (value: Maybe<TFilteredValue>, metadata: PossibleRegleRuleMetadataConsumer) => string | string[];
type?: string;
exec: (value: Maybe<TFilteredValue>) => TAsync extends false ? TMetaData : Promise<TMetaData>;
}
Expand All @@ -37,6 +34,7 @@ export interface $InternalRegleRuleDefinition extends RegleInternalRuleDefs<any,
validator: RegleRuleDefinitionProcessor;
message: RegleRuleDefinitionWithMetadataProcessor<any, any, any>;
active: RegleRuleDefinitionWithMetadataProcessor<any, any, any>;
tooltip: RegleRuleDefinitionWithMetadataProcessor<any, any, any>;
type?: string;
exec: (value: any) => RegleRuleMetadataDefinition | Promise<RegleRuleMetadataDefinition>;
}
Expand Down
27 changes: 16 additions & 11 deletions packages/core/src/types/rules/rule.init.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import type {
RegleRuleMetadataDefinition,
} from './rule.definition.type';

export type RegleInitPropertyGetter<
TValue,
TReturn,
TParams extends [...any[]],
TMetadata extends RegleRuleMetadataDefinition,
> = TReturn | ((value: Maybe<TValue>, metadata: RegleRuleMetadataConsumer<TParams, TMetadata>) => TReturn);

/**
* @argument
* createRule arguments options
Expand All @@ -16,13 +23,11 @@ export interface RegleRuleInit<
TMetadata extends RegleRuleMetadataDefinition = RegleRuleMetadataDefinition,
TAsync extends boolean = TReturn extends Promise<any> ? true : false,
> {
type?: string;
validator: (value: Maybe<TValue>, ...args: TParams) => TReturn;
message:
| string
| string[]
| ((value: Maybe<TValue>, metadata: RegleRuleMetadataConsumer<TParams, TMetadata>) => string | string[]);
active?: boolean | ((value: Maybe<TValue>, metadata: RegleRuleMetadataConsumer<TParams, TMetadata>) => boolean);
message: RegleInitPropertyGetter<TValue, string | string[], TParams, TMetadata>;
active?: RegleInitPropertyGetter<TValue, boolean, TParams, TMetadata>;
tooltip?: RegleInitPropertyGetter<TValue, string | string[], TParams, TMetadata>;
type?: string;
}

/**
Expand All @@ -36,10 +41,9 @@ export interface RegleRuleCore<
TMetadata extends RegleRuleMetadataDefinition = boolean,
> {
validator: (value: Maybe<TValue>, ...args: TParams) => TAsync extends false ? TMetadata : Promise<TMetadata>;
message:
| string
| ((value: Maybe<TValue>, metadata: RegleRuleMetadataConsumer<TParams, TMetadata>) => string | string[]);
active?: boolean | ((value: Maybe<TValue>, metadata: RegleRuleMetadataConsumer<TParams, TMetadata>) => boolean);
message: RegleInitPropertyGetter<TValue, string | string[], TParams, TMetadata>;
active?: RegleInitPropertyGetter<TValue, string | string[], TParams, TMetadata>;
tooltip?: RegleInitPropertyGetter<TValue, string | string[], TParams, TMetadata>;
type?: string;
}

Expand All @@ -49,8 +53,9 @@ export interface RegleRuleCore<
*/
export interface $InternalRegleRuleInit {
validator: (value: any, ...args: any[]) => RegleRuleMetadataDefinition | Promise<RegleRuleMetadataDefinition>;
message: string | ((value: any, metadata: $InternalRegleRuleMetadataConsumer) => string | string[]);
message: string | string[] | ((value: any, metadata: $InternalRegleRuleMetadataConsumer) => string | string[]);
active?: boolean | ((value: any, metadata: $InternalRegleRuleMetadataConsumer) => boolean);
tooltip?: string | string[] | ((value: any, metadata: $InternalRegleRuleMetadataConsumer) => string | string[]);
type?: string;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/types/rules/rule.internal.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export interface RegleInternalRuleDefs<
| string[]
| ((value: Maybe<TValue>, metadata: PossibleRegleRuleMetadataConsumer) => string | string[]);
_active?: boolean | ((value: Maybe<TValue>, metadata: PossibleRegleRuleMetadataConsumer) => boolean);
_tooltip?:
| string
| string[]
| ((value: Maybe<TValue>, metadata: PossibleRegleRuleMetadataConsumer) => string | string[]);
_type?: string;
_patched: boolean;
_params?: RegleUniversalParams<TParams>;
Expand Down
Loading

0 comments on commit f7d3474

Please sign in to comment.