From 6034f7890da325675983da35d26c117a68e45774 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Thu, 19 Mar 2026 10:24:16 +1100 Subject: [PATCH 1/4] fix: live editor width --- apps/docs/src/components/demo-block/demo-block.scss | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/docs/src/components/demo-block/demo-block.scss b/apps/docs/src/components/demo-block/demo-block.scss index d4f6f31d..4637cec9 100644 --- a/apps/docs/src/components/demo-block/demo-block.scss +++ b/apps/docs/src/components/demo-block/demo-block.scss @@ -32,7 +32,8 @@ &__editor-container { background-color: var(--ty-color-fill); border-top: solid 1px var(--ty-color-border-secondary); - overflow-x: auto; + // Wrap editor text instead of forcing horizontal scrolling. + overflow-x: hidden; font-family: Menlo, Consolas, "Droid Sans Mono", monospace; font-size: 13px; line-height: 1.6; @@ -40,7 +41,6 @@ } &__editor-wrapper { - max-width: 800px; width: 100%; } @@ -50,6 +50,10 @@ pre { margin: 0; pointer-events: none; + // Ensure long lines wrap in the highlighted background layer. + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: anywhere; } } @@ -73,7 +77,10 @@ font-size: inherit; line-height: inherit; tab-size: inherit; - white-space: pre; + // Keep caret + selection aligned with the highlighted background layer. + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: anywhere; box-sizing: border-box; &::selection { From e6f138cf90a11ea7fa02a7711876f91cccbcd352 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Thu, 19 Mar 2026 10:36:17 +1100 Subject: [PATCH 2/4] fix: Form docs --- packages/react/src/form/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react/src/form/index.md b/packages/react/src/form/index.md index 8cac5b54..91c66956 100644 --- a/packages/react/src/form/index.md +++ b/packages/react/src/form/index.md @@ -115,12 +115,15 @@ Simulate an async form submission with loading state. + ### Other Form Controls A versatile example. + + ## API ### Form From 883474a79c98f141f80de907bc183be0451b523a Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Thu, 19 Mar 2026 14:05:33 +1100 Subject: [PATCH 3/4] chore: update diagram --- packages/react/src/form/ARCHITECTURE.md | 133 ++++++++++++++++++++---- 1 file changed, 113 insertions(+), 20 deletions(-) diff --git a/packages/react/src/form/ARCHITECTURE.md b/packages/react/src/form/ARCHITECTURE.md index c0f7af26..5e65bd4e 100644 --- a/packages/react/src/form/ARCHITECTURE.md +++ b/packages/react/src/form/ARCHITECTURE.md @@ -68,7 +68,7 @@ The `validate` function checks a value against a `Rule` supporting: `required`, ```mermaid graph TD - subgraph Form["
"] + subgraph Form[" Provider"] FI["FormInstance (useRef)"] FIC["FormInstanceContext.Provider"] FOC["FormOptionsContext.Provider"] @@ -76,48 +76,120 @@ graph TD subgraph FormItem1[""] SUB1["subscribe(listener)"] - CE1["cloneElement(child, {value, onChange, onBlur})"] + CE1["cloneElement → {value, onChange, onBlur}"] + STATE1["local state: value, error"] ERR1["Error display with Transition"] end subgraph FormItem2[""] SUB2["subscribe(listener)"] - CE2["cloneElement(child, {value, onChange, onBlur})"] + CE2["cloneElement → {value, onChange, onBlur}"] + STATE2["local state: value, error"] ERR2["Error display with Transition"] end + subgraph Input1[""] + V1["value prop (controlled)"] + end + + subgraph Input2[""] + V2["value prop (controlled)"] + end + FI --> FIC + FI --> FOC FIC --> FormItem1 FIC --> FormItem2 FOC --> FormItem1 FOC --> FormItem2 + + FormItem1 --> SUB1 + FormItem1 --> CE1 + FormItem1 --> STATE1 + CE1 --> V1 + STATE1 --> ERR1 + + FormItem2 --> SUB2 + FormItem2 --> CE2 + FormItem2 --> STATE2 + CE2 --> V2 + STATE2 --> ERR2 +``` + +### Subscribe/Notify Pattern (Pub/Sub) + +```mermaid +graph LR + subgraph FormInstance["FormInstance (Central Store)"] + VALUES["values: { username: '', password: '' }"] + ERRORS["errors: { email: ['Invalid email'] }"] + RULES["rules: { username: [...], email: [...] }"] + LISTENERS["listeners: [listener1, listener2, ...]"] + NOTIFY["notify(name)"] + end + + subgraph FormItem_A["Form.Item(name='username')"] + SUB_A["subscribe(callback)"] + CALLBACK_A["callback(n) { if n==='username' setValue() }"] + end + + subgraph FormItem_B["Form.Item(name='email')"] + SUB_B["subscribe(callback)"] + CALLBACK_B["callback(n) { if n==='email' setValue() }"] + end + + subgraph FormItem_C["Form.Item(name='password')"] + SUB_C["subscribe(callback)"] + CALLBACK_C["callback(n) { if n==='password' setValue() }"] + end + + NOTIFY -->|"notify('email')"| LISTENERS + LISTENERS -->|"→ listener_A"| CALLBACK_A + LISTENERS -->|"→ listener_B"| CALLBACK_B + LISTENERS -->|"→ listener_C"| CALLBACK_C + + SUB_A -.->|"adds listener_A"| LISTENERS + SUB_B -.->|"adds listener_B"| LISTENERS + SUB_C -.->|"adds listener_C"| LISTENERS + + style CALLBACK_A fill:#ffcccc + style CALLBACK_B fill:#ccffcc + style CALLBACK_C fill:#ffcccc ``` -### Data Flow: User Input +### Data Flow: User Input → UI Update (Controlled) ```mermaid sequenceDiagram participant User - participant Input as Child Input + participant Input as participant Item as Form.Item participant Store as FormInstance + participant Item2 as Form.Item (other) - Note over Item: On mount: subscribe + setFieldRules + Note over Item: Mount: subscribe(listener) + setFieldRules(name, rules) - User->>Input: Types a character - Input->>Item: onChange fires - Item->>Store: setFieldValue(name, value) - Store->>Store: notify(name) - Store->>Item: Listener callback fires - Item->>Item: setValue → re-render - Item->>Input: cloneElement with new value + User->>Input: Types "a" + Input->>Item: onChange("a") + Item->>Store: setFieldValue("field", "a") + Store->>Store: values["field"] = "a" + Store->>Store: notify("field") + Store->>Item: listener("field") fires + Store->>Item2: listener("field") fires + + Note over Item2: n === "field" but name !== "field"
→ ignore, no update + + Item->>Item: setValue("a") → re-render + Item->>Input: cloneElement injects new value="a" + + Note over Input: Controlled: displays "a" alt validateTrigger === "onChange" - Item->>Store: validateField(name) - Store->>Store: run rules via validate() - Store->>Store: setFieldError + notify - Store->>Item: Listener fires again - Item->>Item: setError → show/hide error + Item->>Store: validateField("field") + Store->>Store: run validate() on rules + Store->>Store: setFieldError + notify("field") + Store->>Item: listener fires again + Item->>Item: setError() → show/hide error end ``` @@ -133,7 +205,7 @@ sequenceDiagram User->>Form: Submit Form->>Form: e.preventDefault() Form->>Store: validateFields() - Store->>Store: validateField() for each rule set + Store->>Store: validateField() for each field with rules Store->>Items: notify each field name Items->>Items: Update error states @@ -161,7 +233,28 @@ sequenceDiagram Store->>Store: errors = {} Store->>Store: values = deepCopy(initValues) Store->>Items: notify("*") - Items->>Items: All items re-read value and error → re-render + Items->>Items: All items: name === "*" or n === "*" → true
→ setValue() + setError() → re-render +``` + +### Controlled Component Pattern + +```mermaid +graph TD + subgraph FormItem["Form.Item"] + STATE["useState
value, error"] + ONCHANGE["onChange(...args)
form.setFieldValue()
form.validateField()"] + CLONE["React.cloneElement
{ value, onChange, onBlur }"] + end + + subgraph Child["Child Input"] + PROP["value prop
(controlled)"] + INTERNAL["no internal state used"] + end + + STATE -->|"on mount"| CLONE + ONCHANGE -->|"on input"| CLONE + CLONE -->|"props"| PROP + PROP -->|"user types"| ONCHANGE ``` ### Validation Rules From 20bb871bea6bc465672873b7adf277047a80f347 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Thu, 19 Mar 2026 14:09:31 +1100 Subject: [PATCH 4/4] chore: update --- packages/react/src/form/ARCHITECTURE.md | 31 +++++++------------------ 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/packages/react/src/form/ARCHITECTURE.md b/packages/react/src/form/ARCHITECTURE.md index 5e65bd4e..a1bea7a6 100644 --- a/packages/react/src/form/ARCHITECTURE.md +++ b/packages/react/src/form/ARCHITECTURE.md @@ -74,26 +74,20 @@ graph TD FOC["FormOptionsContext.Provider"] end - subgraph FormItem1[""] - SUB1["subscribe(listener)"] + subgraph FormItem1["Form.Item (name='username')"] CE1["cloneElement → {value, onChange, onBlur}"] - STATE1["local state: value, error"] - ERR1["Error display with Transition"] end - subgraph FormItem2[""] - SUB2["subscribe(listener)"] + subgraph FormItem2["Form.Item (name='password')"] CE2["cloneElement → {value, onChange, onBlur}"] - STATE2["local state: value, error"] - ERR2["Error display with Transition"] end - subgraph Input1[""] - V1["value prop (controlled)"] + subgraph Input1["Input (controlled by value prop)"] + V1["value: from useState"] end - subgraph Input2[""] - V2["value prop (controlled)"] + subgraph Input2["Input (controlled by value prop)"] + V2["value: from useState"] end FI --> FIC @@ -103,17 +97,8 @@ graph TD FOC --> FormItem1 FOC --> FormItem2 - FormItem1 --> SUB1 - FormItem1 --> CE1 - FormItem1 --> STATE1 - CE1 --> V1 - STATE1 --> ERR1 - - FormItem2 --> SUB2 - FormItem2 --> CE2 - FormItem2 --> STATE2 - CE2 --> V2 - STATE2 --> ERR2 + FormItem1 --> CE1 --> V1 + FormItem2 --> CE2 --> V2 ``` ### Subscribe/Notify Pattern (Pub/Sub)