Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Vue Devtools #116

Merged
merged 27 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
76d5ea2
feat: add dev-tools package with initial configuration and dependencies
s8n11c Jan 25, 2025
50024f7
feat: implement dev-tools package with core functionalities and types
s8n11c Jan 28, 2025
6510d2c
refactor: move helper functions to a new helpers.ts file
s8n11c Jan 28, 2025
0058850
feat: enhance dev-tools with field type resolution and improved tagging
s8n11c Jan 28, 2025
b7626f0
feat: integrate checkbox support in dev-tools with registration
s8n11c Jan 29, 2025
bcb13df
feat: add radio field support to dev-tools with registration
s8n11c Jan 29, 2025
7be32b5
fix: update import paths for dev-tools to use wildcard syntax
s8n11c Jan 29, 2025
26cecb7
refactor: simplify dev-tools registration by removing formId parameter
s8n11c Jan 29, 2025
eb7ccb2
chore: update package name
logaretm Feb 22, 2025
76e4bba
refactor: simplify the API
logaretm Feb 22, 2025
046d840
refactor: use the new api calls and add devtools as dep
logaretm Feb 22, 2025
4193341
fix: eliminate circular dependency
logaretm Feb 22, 2025
6c9509f
feat: hook all fields to devtools
logaretm Feb 22, 2025
4d51c8f
fix: remove standalone tag
logaretm Feb 22, 2025
2065079
chore: test playground forms with no forms
logaretm Feb 22, 2025
d0e108d
fix: ensure fields appear on mount
logaretm Feb 22, 2025
0fb1ef7
fix: added klona and typefest as deps for devtools
logaretm Feb 22, 2025
23c3687
chore: ensure klona is external to devtools
logaretm Feb 22, 2025
f8ac836
chore(playground): remove build failures
logaretm Feb 22, 2025
16060d9
chore: match fw version
logaretm Feb 22, 2025
a6153e9
chore: added changeset
logaretm Feb 22, 2025
c3a8842
fix: only register fields on mounted
logaretm Feb 22, 2025
c0a7ccf
fix: delay registeration logic
logaretm Feb 22, 2025
4f3b55d
feat: branding and form actions
logaretm Feb 22, 2025
9524c53
feat: support filtering
logaretm Feb 22, 2025
91f61ea
feat: render path state
logaretm Feb 22, 2025
4ee0210
fix: make sure devtools is a dependency
logaretm Feb 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"@standard-schema/spec": "1.0.0",
"@standard-schema/utils": "^0.3.0",
"klona": "^2.0.6",
"type-fest": "^4.34.1"
"type-fest": "^4.34.1",
"@formwerk/devtools": "workspace:*"
},
"peerDependencies": {
"vue": ">=3.5.0"
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from './usePicker';
export * from './types';
export * from './config';
export * from './useForm';
export * from './useFormField';
export * from './useFormGroup';
export * from './useFormRepeater';
export * from './validation';
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/useCalendar/useCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import { useInputValidity } from '../validation';
import { fromDateToCalendarZonedDateTime, useTemporalStore } from '../useDateTimeField/useTemporalStore';
import { PickerContextKey } from '../usePicker';
import { registerField } from '@formwerk/devtools';

export interface CalendarProps {
/**
Expand Down Expand Up @@ -105,12 +106,12 @@
/**
* The form field to use for the calendar.
*/
field?: FormField<any>;

Check warning on line 109 in packages/core/src/useCalendar/useCalendar.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

/**
* The schema to use for the calendar.
*/
schema?: StandardSchema<any>;

Check warning on line 114 in packages/core/src/useCalendar/useCalendar.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
}

export function useCalendar(_props: Reactivify<CalendarProps, 'field' | 'schema'>) {
Expand Down Expand Up @@ -366,6 +367,13 @@
);
});

if (__DEV__) {
// If it is its own field, we should register it with devtools.
if (!props.field) {
registerField(field, 'Calendar');
}
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useCheckbox/useCheckbox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { computed, inject, nextTick, Ref, ref, toValue } from 'vue';
import { registerField } from '@formwerk/devtools';
import { hasKeyCode, isEqual, isInputElement, normalizeProps, useUniqId, withRefCapture } from '../utils/common';
import {
AriaLabelableProps,
Expand Down Expand Up @@ -274,6 +275,10 @@ export function useCheckbox<TValue = string>(

const isGrouped = !!group;

if (__DEV__) {
registerField(field, 'Checkbox');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useComboBox/useComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import { useInputValidity } from '../validation';
import { FilterFn } from '../collections';
import { useControlButtonProps } from '../helpers/useControlButtonProps';
import { registerField } from '@formwerk/devtools';

export interface ComboBoxProps<TOption, TValue = TOption> {
/**
Expand Down Expand Up @@ -178,7 +179,7 @@
}

// If an option was clicked, then it would blur the field and so we want to select the clicked option via the `relatedTarget` property.
let relatedTarget = (evt as any).relatedTarget as HTMLElement | null;

Check warning on line 182 in packages/core/src/useComboBox/useComboBox.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
if (relatedTarget) {
relatedTarget = relatedTarget.closest('[role="option"]') as HTMLElement | null;
const opt = renderedOptions.value.find(opt => opt.id === relatedTarget?.id);
Expand Down Expand Up @@ -369,6 +370,10 @@
watch(inputValue, debounce(filter.debounceMs, updateHiddenState));
}

if (__DEV__) {
registerField(field, 'ComboBox');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useCustomField/useCustomField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { exposeField, useFormField } from '../useFormField';
import { createDescribedByProps, normalizeProps, propsToValues, useUniqId, withRefCapture } from '../utils/common';
import { useLabel, useErrorMessage } from '../a11y';
import { useInputValidity } from '../validation';
import { registerField } from '@formwerk/devtools';

export interface CustomFieldProps<TValue = unknown> {
/**
Expand Down Expand Up @@ -97,6 +98,10 @@ export function useCustomField<TValue = unknown>(
),
);

if (__DEV__) {
registerField(field, 'Custom');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useDateTimeField/useDateTimeField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { fromDateToCalendarZonedDateTime, useTemporalStore } from './useTemporal
import { ZonedDateTime, Calendar } from '@internationalized/date';
import { useInputValidity } from '../validation';
import { createDisabledContext } from '../helpers/createDisabledContext';
import { registerField } from '@formwerk/devtools';

export interface DateTimeFieldProps {
/**
Expand Down Expand Up @@ -182,6 +183,10 @@ export function useDateTimeField(_props: Reactivify<DateTimeFieldProps, 'schema'
);
});

if (__DEV__) {
registerField(field, 'Date');
}

return exposeField(
{
/**
Expand Down
18 changes: 16 additions & 2 deletions packages/core/src/useForm/useForm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InjectionKey, MaybeRefOrGetter, onMounted, provide, reactive, readonly, Ref, ref } from 'vue';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import { registerForm } from '@formwerk/devtools';
import { cloneDeep, useUniqId } from '../utils/common';
import {
FormObject,
Expand Down Expand Up @@ -284,8 +285,21 @@ export function useForm<
formProps,
};

return {
const form = {
...baseReturns,
...actions,
} as typeof baseReturns & FormActions<TInput, TOutput>;
};

if (__DEV__) {
// using any until we can figure out how to type this properly
// eslint-disable-next-line @typescript-eslint/no-explicit-any
registerForm(form as any);
}

return form as typeof baseReturns & FormActions<TInput, TOutput>;
}

/**
* Just a utility type helper to get the return type of the useForm composable.
*/
export type FormReturns = ReturnType<typeof useForm>;
3 changes: 2 additions & 1 deletion packages/core/src/useFormField/useFormField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type FormField<TValue> = {
setTouched: (touched: boolean) => void;
setErrors: (messages: Arrayable<string>) => void;
displayError: () => string | undefined;
form?: FormContext | null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename this to resolvedForm for clarity.

};

export function useFormField<TValue = unknown>(opts?: Partial<FormFieldOptions<TValue>>): FormField<TValue> {
Expand Down Expand Up @@ -200,7 +201,7 @@ export function useFormField<TValue = unknown>(opts?: Partial<FormFieldOptions<T
form.setFieldDisabled(path, disabled);
});

return field;
return { ...field, form };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to spread, just add the resolvedForm to the FormField type and then you could just set it as a prop here.

}

function useFieldValidity(getPath: Getter<string | undefined>, isDisabled: Ref<boolean>, form?: FormContext | null) {
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useHiddenField/useHiddenField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Reactivify } from '../types';
import { exposeField, useFormField } from '../useFormField';
import { normalizeProps } from '../utils/common';
import { useInputValidity } from '../validation';
import { registerField } from '@formwerk/devtools';

export interface HiddenFieldProps<TValue = unknown> {
/**
Expand Down Expand Up @@ -41,5 +42,9 @@ export function useHiddenField<TValue = unknown>(_props: Reactivify<HiddenFieldP
},
);

if (__DEV__) {
registerField(field, 'Hidden');
}

return exposeField({}, field);
}
5 changes: 5 additions & 0 deletions packages/core/src/useNumberField/useNumberField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { useLocale } from '../i18n';
import { exposeField, useFormField } from '../useFormField';
import { FieldTypePrefixes } from '../constants';
import { useEventListener } from '../helpers/useEventListener';
import { registerField } from '@formwerk/devtools';

export interface NumberInputDOMAttributes {
name?: string;
Expand Down Expand Up @@ -298,6 +299,10 @@ export function useNumberField(
{ disabled: () => isDisabled.value || toValue(props.disableWheel), passive: true },
);

if (__DEV__) {
registerField(field, 'Number');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useRadio/useRadioGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InjectionKey, toValue, computed, onBeforeUnmount, reactive, provide, ref } from 'vue';
import { registerField } from '@formwerk/devtools';
import { useInputValidity } from '../validation/useInputValidity';
import { useLabel, useErrorMessage } from '../a11y';
import {
Expand Down Expand Up @@ -268,6 +269,10 @@ export function useRadioGroup<TValue = string>(_props: Reactivify<RadioGroupProp

provide(RadioGroupKey, context);

if (__DEV__) {
registerField(field, 'Radio');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useSearchField/useSearchField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useLabel, useErrorMessage } from '../a11y';
import { useFormField, exposeField } from '../useFormField';
import { FieldTypePrefixes } from '../constants';
import { createDisabledContext } from '../helpers/createDisabledContext';
import { registerField } from '@formwerk/devtools';

export interface SearchInputDOMAttributes extends TextInputBaseAttributes {
type?: 'search';
Expand Down Expand Up @@ -227,6 +228,10 @@ export function useSearchField(
),
);

if (__DEV__) {
registerField(field, 'Search');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useSelect/useSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useInputValidity } from '../validation';
import { useListBox } from '../useListBox';
import { useLabel, useErrorMessage } from '../a11y';
import { FieldTypePrefixes } from '../constants';
import { registerField } from '@formwerk/devtools';

export interface SelectProps<TOption, TValue = TOption> {
/**
Expand Down Expand Up @@ -273,6 +274,10 @@ export function useSelect<TOption, TValue = TOption>(_props: Reactivify<SelectPr
);
});

if (__DEV__) {
registerField(field, 'Select');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useSlider/useSlider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { useLocale } from '../i18n';
import { useFormField, exposeField } from '../useFormField';
import { FieldTypePrefixes } from '../constants';
import { useInputValidity } from '../validation';
import { registerField } from '@formwerk/devtools';

export interface SliderProps<TValue = number> {
/**
Expand Down Expand Up @@ -479,6 +480,10 @@ export function useSlider<TValue>(_props: Reactivify<SliderProps<TValue>, 'schem
});
}

if (__DEV__) {
registerField(field, 'Slider');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useSwitch/useSwitch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useLabel, useErrorMessage } from '../a11y';
import { useFormField, exposeField } from '../useFormField';
import { FieldTypePrefixes } from '../constants';
import { useInputValidity } from '../validation';
import { registerField } from '@formwerk/devtools';

export interface SwitchDomInputProps
extends InputBaseAttributes,
Expand Down Expand Up @@ -235,6 +236,10 @@ export function useSwitch<TValue = boolean>(
isPressed.value = force ?? !isPressed.value;
}

if (__DEV__) {
registerField(field, 'Switch');
}

return exposeField(
{
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/useTextField/useTextField.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Ref, computed, shallowRef, toValue } from 'vue';
import { registerField } from '@formwerk/devtools';
import { createDescribedByProps, normalizeProps, propsToValues, useUniqId, withRefCapture } from '../utils/common';
import {
AriaDescribableProps,
Expand Down Expand Up @@ -177,6 +178,10 @@ export function useTextField(
);
});

if (__DEV__) {
registerField(field, 'Text');
}

return exposeField(
{
/**
Expand Down
45 changes: 45 additions & 0 deletions packages/devtools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@formwerk/devtools",
"version": "0.8.0",
"description": "Formwerk's Vue devtools plugin",
"author": "Abdelrahman Awad <[email protected]>",
"license": "MIT",
"funding": "https://github.com/sponsors/logaretm",
"module": "dist/devtools.mjs",
"unpkg": "dist/devtools.iife.js",
"jsdelivr": "dist/devtools.iife.js",
"main": "dist/index.mjs",
"types": "dist/devtools.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/devtools.mjs",
"require": "./dist/devtools.cjs",
"types": "./dist/devtools.d.ts"
},
"./dist/index.d.ts": "./dist/devtools.d.ts"
},
"sideEffects": false,
"keywords": [
"VueJS",
"Vue",
"validation",
"validator",
"inputs",
"form"
],
"files": [
"dist/*.js",
"dist/*.mjs",
"dist/*.cjs",
"dist/*.d.ts"
],
"dependencies": {
"klona": "^2.0.6",
"type-fest": "^4.34.1"
},
"peerDependencies": {
"@vue/devtools-api": "^7.5.2",
"@vue/devtools-kit": "^7.5.2"
}
}
20 changes: 20 additions & 0 deletions packages/devtools/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function getPluginColors() {
return {
error: 0xbd4b4b,
success: 0x06d77b,
unknown: 0x54436b,
white: 0xffffff,
black: 0x000000,
blue: 0x035397,
purple: 0xb980f0,
orange: 0xf5a962,
gray: 0xbbbfca,
};
}
export function getInspectorId() {
return 'formwerk-inspector';
}

export function getRootFormId() {
return '~none';
}
Loading
Loading