Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion content/docs/form/advanced/normalized-store.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "정규화된 필드 기록이 getState, getValues, 어댑터 구

# 정규화 저장소

관찰 가능한 내부 상태는 `FormState<TValues>` 모양입니다. 여기에는 `initialValues`, `fields`, `submitCount`, `arrayKeys`가 있습니다. `fields` 기록은 정규화된 경로 키로 저장되며 말단 값마다 하나의 `FieldState`를 가집니다. 그래서 `getFieldState(path)`가 가볍게 동작하고, 어댑터가 하나의 스냅샷에서 errors, dirty fields, touched fields를 요약할 수 있습니다.
관찰 가능한 내부 상태는 `FormState<TValues>` 모양입니다. 여기에는 `defaultValues`, `fields`, `submitCount`, `arrayKeys`가 있습니다. `fields` 기록은 정규화된 경로 키로 저장되며 말단 값마다 하나의 `FieldState`를 가집니다. 그래서 `getFieldState(path)`가 가볍게 동작하고, 어댑터가 하나의 스냅샷에서 errors, dirty fields, touched fields를 요약할 수 있습니다.

```ts lineNumbers
const snapshot = form.getState();
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/advanced/normalized-store.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "How normalized field records explain getState, getValues, and adap

# Normalized store

Internally visible state is shaped as `FormState<TValues>`: `initialValues`, `fields`, `submitCount`, and `arrayKeys`. The `fields` record is keyed by normalized path keys and stores one `FieldState` per leaf. This is why `getFieldState(path)` can be cheap and why adapters can summarize errors, dirty fields, and touched fields from one snapshot.
Internally visible state is shaped as `FormState<TValues>`: `defaultValues`, `fields`, `submitCount`, and `arrayKeys`. The `fields` record is keyed by normalized path keys and stores one `FieldState` per leaf. This is why `getFieldState(path)` can be cheap and why adapters can summarize errors, dirty fields, and touched fields from one snapshot.

```ts lineNumbers
const snapshot = form.getState();
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/guides/build-a-login-form.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: "필드 바인딩, 검증, 제출, 초기화를 갖춘 실무형

```tsx lineNumbers
const form = new CreateForm({
initialValues: { email: '', password: '', remember: false },
defaultValues: { email: '', password: '', remember: false },
schema: loginSchema,
validateOn: ['blur', 'submit'],
});
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/guides/build-a-login-form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Start by keeping the form instance outside the component so it is not recreated

```tsx lineNumbers
const form = new CreateForm({
initialValues: { email: '', password: '', remember: false },
defaultValues: { email: '', password: '', remember: false },
schema: loginSchema,
validateOn: ['blur', 'submit'],
});
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/guides/validation-flow.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: "validateOn, field-local 스키마, 수동 trigger, 제출 검증

```ts lineNumbers
const form = new CreateForm({
initialValues,
defaultValues,
schema: profileSchema,
validateOn: ['blur', 'submit'],
});
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/guides/validation-flow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A practical validation flow usually combines three layers. First, a form-level S

```ts lineNumbers
const form = new CreateForm({
initialValues,
defaultValues,
schema: profileSchema,
validateOn: ['blur', 'submit'],
});
Expand Down
2 changes: 1 addition & 1 deletion content/docs/form/index.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ description: "프레임워크와 분리된 폼 상태, 검증, 배열 메타데
import { CreateForm } from '@ilokesto/form';

const form = new CreateForm({
initialValues: {
defaultValues: {
email: '',
profile: { name: 'Ada' },
tags: ['docs'],
Expand Down
4 changes: 3 additions & 1 deletion content/docs/form/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Install the framework peer you use as well: `react`, `vue`, `solid-js`, or `svel
import { CreateForm } from '@ilokesto/form';

const form = new CreateForm({
initialValues: {
defaultValues: {
email: '',
profile: { name: 'Ada' },
tags: ['docs'],
Expand All @@ -48,3 +48,5 @@ The most important rule is path semantics. A string is a literal field name, so
## Where to go next

Read [Quick start](/en/form/quick-start) for a working React example, [Core concepts](/en/form/core-concepts) for the mental model, [CreateForm](/en/form/reference/create-form) for the core API, and the integration page for your framework when you are ready to bind DOM inputs.

> This documentation lives in the `@ilokesto/form` repository and is synced to the ilokesto docs site.
14 changes: 14 additions & 0 deletions content/docs/form/integrations/react.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ function ProfileForm({ form }) {

text input은 `onChange`로 갱신됩니다. checkbox와 radio는 checked 상태를 사용합니다. select와 textarea 바인딩은 제네릭으로 좁힐 수 있습니다. 오류를 렌더링하거나 `setValue`를 호출해야 하면 `useField`를 쓰고, 평범한 입력에 속성만 필요하면 `useRegister`를 사용하세요.

## 외부 값 동기화

`ReactFormOptions`는 현재 render의 `values`와 plain `resetOptions`를 받습니다. 처음 정의된 값과 이후 `Object.is` identity가 달라진 값마다 `form.reset(values, resetOptions)`를 호출합니다. `undefined`는 reset 없이 동기화를 중단하고, 그 뒤 마지막으로 정의되었던 같은 object를 다시 전달해도 no-op입니다. `resetOptions`만 바꾸는 것도 no-op이며 새 `values` reference가 reset을 일으킬 때만 읽힙니다.

```tsx
const { form } = useForm({
defaultValues: emptyProfile,
values: query.data,
resetOptions: { keepDirtyValues: true },
});
```

`defaultValues`는 component-owned form을 한 번만 생성합니다. values effect는 component unmount 시 종료됩니다. `useForm(existingForm)` overload는 기존 동작을 유지하며 external-value synchronization을 설치하지 않습니다.

## 주의할 점

코어 폼은 안정적인 위치에서 만들고, 해당 프레임워크 피어 의존성을 설치하며, 프레임워크 컴포넌트에서는 이 어댑터 하위 경로에서만 가져오세요. 서버 액션이나 도메인 도우미처럼 렌더링이 필요 없는 코드는 루트 `@ilokesto/form` API에 남겨두는 편이 좋습니다.
14 changes: 14 additions & 0 deletions content/docs/form/integrations/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ function ProfileForm({ form }) {

Text inputs update through `onChange`; checkbox and radio fields use checked state; select and textarea bindings can be narrowed with a generic. Use `useField` when rendering errors or calling `setValue`, and `useRegister` when a plain input only needs props.

## Syncing external values

`ReactFormOptions` accepts the current render `values` and plain `resetOptions`. The first defined value and each later value with a different `Object.is` identity call `form.reset(values, resetOptions)`. `undefined` pauses synchronization without resetting, and passing the last defined object again remains a no-op. Changing only `resetOptions` is also a no-op; it is read only when a new `values` reference drives a reset.

```tsx
const { form } = useForm({
defaultValues: emptyProfile,
values: query.data,
resetOptions: { keepDirtyValues: true },
});
```

`defaultValues` creates the component-owned form once. The values effect ends when the component unmounts. `useForm(existingForm)` keeps its existing overload behavior and does not install external-value synchronization.

## Cautions

Create the core form in a stable place, install the matching peer dependency, and import only from this adapter subpath in framework components. Keep server actions and domain helpers on the root `@ilokesto/form` API when they do not need rendering.
15 changes: 15 additions & 0 deletions content/docs/form/integrations/solid.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ function Phones({ form }) {

오류 렌더링과 직접 setter가 필요하면 `useField`를 사용하세요. text, checkbox, radio, select, textarea, DOM 호환 커스텀 컨트롤에는 `useRegister`를 사용합니다. React에서처럼 배열 키와 튜플 인덱스 경로는 분리해서 관리하세요.

## 외부 값 동기화

`SolidFormOptions`는 `values`를 `Accessor<T | undefined>`로 받고 plain `resetOptions`를 받습니다. 처음 정의된 값과 이후 `Object.is` identity가 달라진 값마다 `form.reset(values, resetOptions)`를 호출합니다. `undefined`는 reset 없이 동기화를 중단하고, 마지막으로 정의되었던 같은 object를 다시 emit해도 no-op입니다. `resetOptions`는 value-driven reset에만 적용됩니다.

```tsx
const [serverValues, setServerValues] = createSignal<Profile | undefined>();
const { form } = useForm({
defaultValues: emptyProfile,
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
```

이 overload에는 active Solid owner가 필요하며 owner가 없으면 form 생성 전에 실패합니다. Tracking computation은 해당 owner와 함께 dispose됩니다. `useForm(existingForm)`은 values tracking을 설치하지 않습니다.

## 주의할 점

코어 폼은 안정적인 위치에서 만들고, 해당 프레임워크 피어 의존성을 설치하며, 프레임워크 컴포넌트에서는 이 어댑터 하위 경로에서만 가져오세요. 서버 액션이나 도메인 도우미처럼 렌더링이 필요 없는 코드는 루트 `@ilokesto/form` API에 남겨두는 편이 좋습니다.
15 changes: 15 additions & 0 deletions content/docs/form/integrations/solid.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ function Phones({ form }) {

Use `useField` for error rendering and direct setters. Use `useRegister` for text, checkbox, radio, select, textarea, and DOM-compatible custom controls. Keep array keys separate from tuple index paths, just as you would in React.

## Syncing external values

`SolidFormOptions` accepts `values` as `Accessor<T | undefined>` and accepts plain `resetOptions`. The first defined value and each later value with a different `Object.is` identity call `form.reset(values, resetOptions)`. `undefined` pauses synchronization without resetting, and re-emitting the last defined object remains a no-op. `resetOptions` applies only to value-driven resets.

```tsx
const [serverValues, setServerValues] = createSignal<Profile | undefined>();
const { form } = useForm({
defaultValues: emptyProfile,
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
```

This overload requires an active Solid owner and fails before form creation when none exists. The tracking computation is disposed with that owner. `useForm(existingForm)` does not install values tracking.

## Cautions

Create the core form in a stable place, install the matching peer dependency, and import only from this adapter subpath in framework components. Keep server actions and domain helpers on the root `@ilokesto/form` API when they do not need rendering.
20 changes: 20 additions & 0 deletions content/docs/form/integrations/svelte.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ description: "@ilokesto/form/svelte의 register 액션과 읽기 가능한 폼

액션은 DOM 리스너를 붙이고 요소가 사라질 때 정리합니다. `useFormState()`는 errors, dirty fields, touched fields, validity, submitting state, submitCount 같은 전체 상태 요약을 담은 Svelte readable store를 반환합니다.

## 외부 값 동기화

`SvelteFormOptions`는 `values`를 `Readable<T | undefined>`로 받고 plain `resetOptions`를 받습니다. 처음 정의된 emission과 이후 `Object.is` identity가 달라진 emission마다 `form.reset(values, resetOptions)`를 호출합니다. `undefined`는 reset 없이 동기화를 중단하고, 마지막으로 정의되었던 같은 object를 다시 emit해도 no-op입니다. `resetOptions`는 value-driven reset에만 적용됩니다.

```svelte
<script lang="ts">
import { writable } from 'svelte/store';
import { useForm } from '@ilokesto/form/svelte';

const serverValues = writable<Profile | undefined>(undefined);
const { form } = useForm({
defaultValues: emptyProfile,
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
</script>
```

이 options overload는 component initialization 중 호출해야 합니다. Readable subscription은 component unmount 시 해제됩니다. `useForm(existingForm)`은 external values를 구독하지 않습니다.

## 주의할 점

코어 폼은 안정적인 위치에서 만들고, 해당 프레임워크 피어 의존성을 설치하며, 프레임워크 컴포넌트에서는 이 어댑터 하위 경로에서만 가져오세요. 서버 액션이나 도메인 도우미처럼 렌더링이 필요 없는 코드는 루트 `@ilokesto/form` API에 남겨두는 편이 좋습니다.
20 changes: 20 additions & 0 deletions content/docs/form/integrations/svelte.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ Install `svelte` as the peer and import from `@ilokesto/form/svelte`. Svelte is

The action attaches DOM listeners and cleans them up when the element is destroyed. `useFormState()` returns a Svelte readable store containing aggregate state such as errors, dirty fields, touched fields, validity, submitting state, and submitCount.

## Syncing external values

`SvelteFormOptions` accepts `values` as `Readable<T | undefined>` and accepts plain `resetOptions`. The first defined emission and each later emission with a different `Object.is` identity call `form.reset(values, resetOptions)`. `undefined` pauses synchronization without resetting, and re-emitting the last defined object remains a no-op. `resetOptions` applies only to value-driven resets.

```svelte
<script lang="ts">
import { writable } from 'svelte/store';
import { useForm } from '@ilokesto/form/svelte';

const serverValues = writable<Profile | undefined>(undefined);
const { form } = useForm({
defaultValues: emptyProfile,
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
</script>
```

Call this options overload during component initialization. Its readable subscription is released on component unmount. `useForm(existingForm)` does not subscribe to external values.

## Cautions

Create the core form in a stable place, install the matching peer dependency, and import only from this adapter subpath in framework components. Keep server actions and domain helpers on the root `@ilokesto/form` API when they do not need rendering.
20 changes: 20 additions & 0 deletions content/docs/form/integrations/vue.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ const state = useFormState();

반환된 상태는 getter 기반으로 읽히므로 템플릿에서 최신 값을 볼 수 있습니다. field-local 스키마는 현재 Vue effect scope와 함께 정리됩니다. 커스텀 컴포넌트에서는 DOM과 호환되는 value, checked, 이벤트 동작을 통과시키세요.

## 외부 값 동기화

`VueFormOptions`는 `values`를 `MaybeRefOrGetter<T | undefined>`로 받고 plain `resetOptions`를 받습니다. 처음 정의된 값과 이후 `Object.is` identity가 달라진 값마다 `form.reset(values, resetOptions)`를 호출합니다. `undefined`는 reset 없이 동기화를 중단하고, 마지막으로 정의되었던 같은 object를 다시 emit해도 no-op입니다. `resetOptions`는 value-driven reset에만 사용됩니다.

```vue
<script setup lang="ts">
import { ref } from 'vue';
import { useForm } from '@ilokesto/form/vue';

const serverValues = ref({ email: 'initial@example.com' });
const { form, useRegister } = useForm({
defaultValues: { email: '' },
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
</script>
```

변화 추적을 보장하려면 반응형 소스(`ref`/`computed`/getter)를 직접 전달하세요. 평면 값은 생성 시 한 번만 평가됩니다. 이 overload에는 active Vue effect scope가 필요하며 scope가 없으면 form 생성 전에 실패합니다. Watcher는 해당 scope와 함께 중지되며 `useForm(existingForm)`은 values watcher를 설치하지 않습니다.

## 주의할 점

코어 폼은 안정적인 위치에서 만들고, 해당 프레임워크 피어 의존성을 설치하며, 프레임워크 컴포넌트에서는 이 어댑터 하위 경로에서만 가져오세요. 서버 액션이나 도메인 도우미처럼 렌더링이 필요 없는 코드는 루트 `@ilokesto/form` API에 남겨두는 편이 좋습니다.
20 changes: 20 additions & 0 deletions content/docs/form/integrations/vue.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ const state = useFormState();

The returned state uses getter-backed reads so templates see fresh values. Field-local schemas are cleaned up with the current Vue effect scope. For custom components, pass through DOM-compatible value, checked, and event behavior.

## Syncing external values

`VueFormOptions` accepts `values` as `MaybeRefOrGetter<T | undefined>` and accepts plain `resetOptions`. The first defined value and each later value with a different `Object.is` identity call `form.reset(values, resetOptions)`. `undefined` pauses synchronization without resetting, and re-emitting the last defined object remains a no-op. `resetOptions` is used only by a value-driven reset.

```vue
<script setup lang="ts">
import { ref } from 'vue';
import { useForm } from '@ilokesto/form/vue';

const serverValues = ref({ email: 'initial@example.com' });
const { form, useRegister } = useForm({
defaultValues: { email: '' },
values: serverValues,
resetOptions: { keepDirtyValues: true },
});
</script>
```

Pass reactive sources (`ref`/`computed`/getter) directly so Vue can track changes. A plain value is evaluated once. This overload requires an active Vue effect scope and fails before form creation when none exists. The watcher stops with that scope, and `useForm(existingForm)` does not install a values watcher.

## Cautions

Create the core form in a stable place, install the matching peer dependency, and import only from this adapter subpath in framework components. Keep server actions and domain helpers on the root `@ilokesto/form` API when they do not need rendering.
4 changes: 2 additions & 2 deletions content/docs/form/quick-start.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type LoginValues = {
};

export const loginForm = new CreateForm<LoginValues>({
initialValues: {
defaultValues: {
email: '',
password: '',
remember: false,
Expand All @@ -36,7 +36,7 @@ export const loginForm = new CreateForm<LoginValues>({
});
```

`initialValues`는 reset 기준값이자 처음 필드 값입니다. `validateOn`은 자동 검증이 실행될 시점을 정합니다. 그래도 필요한 순간에는 언제든 `trigger()`를 직접 호출할 수 있습니다.
`defaultValues`는 reset 기준값이자 처음 필드 값입니다. `validateOn`은 자동 검증이 실행될 시점을 정합니다. 그래도 필요한 순간에는 언제든 `trigger()`를 직접 호출할 수 있습니다.

## 3. React에서 필드 연결하기

Expand Down
4 changes: 2 additions & 2 deletions content/docs/form/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type LoginValues = {
};

export const loginForm = new CreateForm<LoginValues>({
initialValues: {
defaultValues: {
email: '',
password: '',
remember: false,
Expand All @@ -36,7 +36,7 @@ export const loginForm = new CreateForm<LoginValues>({
});
```

`initialValues` establishes the reset baseline and the first `FieldState` values. `validateOn` tells the core when automatic validation should run. You can still call `trigger()` manually at any time.
`defaultValues` establishes the reset baseline and the first `FieldState` values. `validateOn` tells the core when automatic validation should run. You can still call `trigger()` manually at any time.

## 3. Bind fields in React

Expand Down
4 changes: 2 additions & 2 deletions content/docs/form/reference/create-form.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ description: "렌더러 없이 폼 컨트롤러를 만들고 상태를 구독하

# CreateForm

`CreateForm<TValues>`는 모든 어댑터 뒤에 있는 코어 클래스입니다. 생성자에는 `CreateFormOptions`를 전달합니다. 옵션에는 `initialValues`, 선택적인 폼 전체 `schema`, 선택적인 `schemaOptions`, 선택적인 `validateOn`이 있습니다. 인스턴스는 `Form<TValues>`를 구현하므로 어댑터와 테스트가 하나의 안정된 계약에 의존할 수 있습니다.
`CreateForm<TValues>`는 모든 어댑터 뒤에 있는 코어 클래스입니다. 생성자에는 `CreateFormOptions`를 전달합니다. 옵션에는 `defaultValues`, 선택적인 폼 전체 `schema`, 선택적인 `schemaOptions`, 선택적인 `validateOn`이 있습니다. 인스턴스는 `Form<TValues>`를 구현하므로 어댑터와 테스트가 하나의 안정된 계약에 의존할 수 있습니다.

```ts lineNumbers
const form = new CreateForm({
initialValues: { email: '', profile: { name: '' } },
defaultValues: { email: '', profile: { name: '' } },
schema: profileSchema,
schemaOptions: { libraryOptions: { abortEarly: false } },
validateOn: ['change', 'blur', 'submit'],
Expand Down
Loading