diff --git a/SKILL.md b/SKILL.md index c1d62ef5d..350858434 100644 --- a/SKILL.md +++ b/SKILL.md @@ -476,7 +476,7 @@ class App : Component } static Element NavBtn(string label, string current, Action set) => - Button(label, () => set(label)).Disabled(label == current); + Button(label, () => set(label)).IsEnabled(!(label == current)); } ``` diff --git a/docs/_pipeline/ai-author-skill.md b/docs/_pipeline/ai-author-skill.md index 0187aa138..c7fedc2dc 100644 --- a/docs/_pipeline/ai-author-skill.md +++ b/docs/_pipeline/ai-author-skill.md @@ -702,7 +702,7 @@ case (no deps); `Memo(ctx => …, deps)` to also re-render when any dep changes; `.Background(color|ThemeRef)`, `.Foreground(color|ThemeRef)`, `.CornerRadius(n)`, `.WithBorder(color|ThemeRef, thickness?)`, `.HAlign(alignment)`, `.VAlign(alignment)`, -`.Disabled(bool)`, `.Visible(bool)`, `.WithKey(string)`, +`.IsEnabled(!bool)`, `.IsVisible(bool)`, `.WithKey(string)`, `.Flex(grow?, shrink?, basis?)`, `.ToolTip(string)`, `.FocusTrap(FocusTrapHandle)`, `.Set(control => { /* raw WinUI access */ })` diff --git a/docs/_pipeline/apps/accessibility/App.cs b/docs/_pipeline/apps/accessibility/App.cs index c34011566..8cd9ccb1a 100644 --- a/docs/_pipeline/apps/accessibility/App.cs +++ b/docs/_pipeline/apps/accessibility/App.cs @@ -83,7 +83,7 @@ public override Element Render() CheckBox(agree, setAgree, label: "I accept the terms") .TabIndex(3), Button("Register", () => { }) - .Disabled(!valid).TabIndex(4).AccessKey("R") + .IsEnabled(valid).TabIndex(4).AccessKey("R") ).Landmark(AutomationLandmarkType.Form).Padding(24); } } diff --git a/docs/_pipeline/apps/forms/App.cs b/docs/_pipeline/apps/forms/App.cs index 10764be14..456a90285 100644 --- a/docs/_pipeline/apps/forms/App.cs +++ b/docs/_pipeline/apps/forms/App.cs @@ -119,11 +119,11 @@ public override Element Render() // commit-on-keystroke, so validation reacts as the user types. NumberBox(age, setAge, header: "Age").Immediate(), - // .DisabledFocusable() keeps the button tab-reachable and + // .IsDisabledFocusable() keeps the button tab-reachable and // visually dimmed while preventing invocation. Pattern mirrors // Fluent UI's `disabledFocusable` and ARIA `aria-disabled`. Button("Submit", () => { /* submit */ }) - .DisabledFocusable(!formValid) + .IsDisabledFocusable(!formValid) .Margin(0, 8, 0, 0) ).Padding(24); } @@ -162,7 +162,7 @@ public override Element Render() { ctx.MarkAllTouched(); if (ctx.IsValid()) setSubmitted(true); - }).Disabled(submitted), + }).IsEnabled(!submitted), When(submitted, () => TextBlock("Registration successful!") .Foreground(Theme.SystemSuccess).SemiBold()) diff --git a/docs/_pipeline/apps/navigation/App.cs b/docs/_pipeline/apps/navigation/App.cs index f797528c1..5682af9ee 100644 --- a/docs/_pipeline/apps/navigation/App.cs +++ b/docs/_pipeline/apps/navigation/App.cs @@ -38,7 +38,7 @@ public override Element Render() Button("Settings", () => nav.Navigate(Route.Settings)), Button("Profile", () => nav.Navigate(Route.Profile)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { @@ -101,9 +101,9 @@ public override Element Render() Button("Reset", () => nav.Reset(Route.Home)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack), + .IsEnabled(nav.CanGoBack), Button("Forward", () => nav.GoForward()) - .Disabled(!nav.CanGoForward) + .IsEnabled(nav.CanGoForward) ), NavigationHost(nav, route => TextBlock($"Page: {route}") @@ -158,7 +158,7 @@ public override Element Render() Button("Settings", () => nav.Navigate(Route.Settings)), Button("Profile", () => nav.Navigate(Route.Profile)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { @@ -256,7 +256,7 @@ public override Element Render() { if (savedState is not null) nav.SetState(savedState); - }).Disabled(savedState is null) + }).IsEnabled(!(savedState is null)) ), TextBlock($"Current: {nav.CurrentRoute}"), TextBlock($"Saved: {savedState?[..Math.Min(50, savedState?.Length ?? 0)] ?? "(none)"}") @@ -322,7 +322,7 @@ public override Element Render() Button("Home", () => nav.Navigate(Route.Home)), Button("Settings", () => nav.Navigate(Route.Settings)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { diff --git a/docs/_pipeline/apps/todo-app/App.cs b/docs/_pipeline/apps/todo-app/App.cs index 91694e4e4..a4c15c143 100644 --- a/docs/_pipeline/apps/todo-app/App.cs +++ b/docs/_pipeline/apps/todo-app/App.cs @@ -35,7 +35,7 @@ public override Element Render() updateItems(list => [.. list, new TodoItem(newText.Trim(), false)]); setNewText(""); } - }).Disabled(string.IsNullOrWhiteSpace(newText)) + }).IsEnabled(!(string.IsNullOrWhiteSpace(newText))) ), // Item list diff --git a/docs/_pipeline/apps/xaml-developers/App.cs b/docs/_pipeline/apps/xaml-developers/App.cs index 6329aaf78..0a6dd79e3 100644 --- a/docs/_pipeline/apps/xaml-developers/App.cs +++ b/docs/_pipeline/apps/xaml-developers/App.cs @@ -49,7 +49,7 @@ public override Element Render() TextField(email, setEmail, header: "Email"), CheckBox(wantsUpdates, setWantsUpdates, label: "Email me updates"), HStack(8, - Button("Save", () => { }).Disabled(!canSave), + Button("Save", () => { }).IsEnabled(canSave), TextBlock(canSave ? "Ready to save" : "Complete all required fields") .Opacity(0.7) ) diff --git a/docs/_pipeline/templates/forms.md.dt b/docs/_pipeline/templates/forms.md.dt index 5b66d3120..fa30a5efd 100644 --- a/docs/_pipeline/templates/forms.md.dt +++ b/docs/_pipeline/templates/forms.md.dt @@ -138,7 +138,7 @@ Reactor offers two opt-in fixes for this. They compose: ![Keep submit reachable](screenshot://forms/keep-submit-reachable) -**`.DisabledFocusable(bool)` on Button** keeps the button keyboard-focusable +**`.IsDisabledFocusable(bool)` on Button** keeps the button keyboard-focusable and tab-reachable while presenting it as disabled (dimmed; the click is suppressed). Mirrors Fluent UI React's `disabledFocusable` and ARIA's `aria-disabled`. Use it for any Submit gated on validation, busy state, or @@ -152,14 +152,14 @@ the right choice when an intermediate value would be expensive or surprising (snapping `2.50` to `2.5` mid-edit). Apply when validation gates UI state and you want it to feel live. -Use `.DisabledFocusable()` whenever a button is conditionally disabled in a +Use `.IsDisabledFocusable()` whenever a button is conditionally disabled in a form — even if you've also applied `.Immediate()` to every commit-on-blur input. The two cover different failure modes: `.Immediate()` keeps validity -in sync with typing; `.DisabledFocusable()` keeps the button discoverable +in sync with typing; `.IsDisabledFocusable()` keeps the button discoverable when validity is gated on async checks, required-but-untouched fields, cross-field rules, or any derived condition that can't be made instantaneous. -> **Where not to use `.DisabledFocusable()`:** only buttons. For data-entry +> **Where not to use `.IsDisabledFocusable()`:** only buttons. For data-entry > controls (`TextField`, `NumberBox`, `CheckBox`, etc.), `IsEnabled=false` > usually means "this field isn't part of your current task" (cascading > from another input), and tab-skipping is the correct UX. Use diff --git a/docs/guide/accessibility.md b/docs/guide/accessibility.md index e2e241144..6ebb8ffe0 100644 --- a/docs/guide/accessibility.md +++ b/docs/guide/accessibility.md @@ -156,7 +156,7 @@ class AccessibleFormDemo : Component CheckBox(agree, setAgree, label: "I accept the terms") .TabIndex(3), Button("Register", () => { }) - .Disabled(!valid).TabIndex(4).AccessKey("R") + .IsEnabled(valid).TabIndex(4).AccessKey("R") ).Landmark(AutomationLandmarkType.Form).Padding(24); } } diff --git a/docs/guide/forms.md b/docs/guide/forms.md index 88cb2b4ef..d33fa8f08 100644 --- a/docs/guide/forms.md +++ b/docs/guide/forms.md @@ -246,11 +246,11 @@ class KeepSubmitReachableDemo : Component // commit-on-keystroke, so validation reacts as the user types. NumberBox(age, setAge, header: "Age").Immediate(), - // .DisabledFocusable() keeps the button tab-reachable and + // .IsDisabledFocusable() keeps the button tab-reachable and // visually dimmed while preventing invocation. Pattern mirrors // Fluent UI's `disabledFocusable` and ARIA `aria-disabled`. Button("Submit", () => { /* submit */ }) - .DisabledFocusable(!formValid) + .IsDisabledFocusable(!formValid) .Margin(0, 8, 0, 0) ).Padding(24); } @@ -259,7 +259,7 @@ class KeepSubmitReachableDemo : Component ![Keep submit reachable](images/forms/keep-submit-reachable.png) -**`.DisabledFocusable(bool)` on Button** keeps the button keyboard-focusable +**`.IsDisabledFocusable(bool)` on Button** keeps the button keyboard-focusable and tab-reachable while presenting it as disabled (dimmed; the click is suppressed). Mirrors Fluent UI React's `disabledFocusable` and ARIA's `aria-disabled`. Use it for any Submit gated on validation, busy state, or @@ -273,14 +273,14 @@ the right choice when an intermediate value would be expensive or surprising (snapping `2.50` to `2.5` mid-edit). Apply when validation gates UI state and you want it to feel live. -Use `.DisabledFocusable()` whenever a button is conditionally disabled in a +Use `.IsDisabledFocusable()` whenever a button is conditionally disabled in a form — even if you've also applied `.Immediate()` to every commit-on-blur input. The two cover different failure modes: `.Immediate()` keeps validity -in sync with typing; `.DisabledFocusable()` keeps the button discoverable +in sync with typing; `.IsDisabledFocusable()` keeps the button discoverable when validity is gated on async checks, required-but-untouched fields, cross-field rules, or any derived condition that can't be made instantaneous. -> **Where not to use `.DisabledFocusable()`:** only buttons. For data-entry +> **Where not to use `.IsDisabledFocusable()`:** only buttons. For data-entry > controls (`TextField`, `NumberBox`, `CheckBox`, etc.), `IsEnabled=false` > usually means "this field isn't part of your current task" (cascading > from another input), and tab-skipping is the correct UX. Use @@ -324,7 +324,7 @@ class ValidationContextDemo : Component { ctx.MarkAllTouched(); if (ctx.IsValid()) setSubmitted(true); - }).Disabled(submitted), + }).IsEnabled(!submitted), When(submitted, () => TextBlock("Registration successful!") .Foreground(Theme.SystemSuccess).SemiBold()) diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 037908c0b..349230f50 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -347,7 +347,7 @@ class TodoApp : Component updateItems(list => [.. list, new TodoItem(newText.Trim(), false)]); setNewText(""); } - }).Disabled(string.IsNullOrWhiteSpace(newText)) + }).IsEnabled(!(string.IsNullOrWhiteSpace(newText))) ), // Item list diff --git a/docs/guide/navigation.md b/docs/guide/navigation.md index 77267f842..f1e9756c7 100644 --- a/docs/guide/navigation.md +++ b/docs/guide/navigation.md @@ -57,7 +57,7 @@ class BasicNavDemo : Component Button("Settings", () => nav.Navigate(Route.Settings)), Button("Profile", () => nav.Navigate(Route.Profile)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { @@ -163,9 +163,9 @@ class StackOperationsDemo : Component Button("Reset", () => nav.Reset(Route.Home)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack), + .IsEnabled(nav.CanGoBack), Button("Forward", () => nav.GoForward()) - .Disabled(!nav.CanGoForward) + .IsEnabled(nav.CanGoForward) ), NavigationHost(nav, route => TextBlock($"Page: {route}") @@ -272,7 +272,7 @@ class PageTransitionsDemo : Component Button("Settings", () => nav.Navigate(Route.Settings)), Button("Profile", () => nav.Navigate(Route.Profile)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { @@ -411,7 +411,7 @@ class PageCachingDemo : Component Button("Home", () => nav.Navigate(Route.Home)), Button("Settings", () => nav.Navigate(Route.Settings)), Button("Back", () => nav.GoBack()) - .Disabled(!nav.CanGoBack) + .IsEnabled(nav.CanGoBack) ), NavigationHost(nav, route => route switch { @@ -514,7 +514,7 @@ class StateSerializationDemo : Component { if (savedState is not null) nav.SetState(savedState); - }).Disabled(savedState is null) + }).IsEnabled(!(savedState is null)) ), TextBlock($"Current: {nav.CurrentRoute}"), TextBlock($"Saved: {savedState?[..Math.Min(50, savedState?.Length ?? 0)] ?? "(none)"}") diff --git a/docs/guide/xaml-developers.md b/docs/guide/xaml-developers.md index 746e69195..819905d3b 100644 --- a/docs/guide/xaml-developers.md +++ b/docs/guide/xaml-developers.md @@ -83,7 +83,7 @@ class TutorialFormPage : Component TextField(email, setEmail, header: "Email"), CheckBox(wantsUpdates, setWantsUpdates, label: "Email me updates"), HStack(8, - Button("Save", () => { }).Disabled(!canSave), + Button("Save", () => { }).IsEnabled(canSave), TextBlock(canSave ? "Ready to save" : "Complete all required fields") .Opacity(0.7) ) diff --git a/docs/reference/async-system.md b/docs/reference/async-system.md index eba64fdc7..260c7ed7c 100644 --- a/docs/reference/async-system.md +++ b/docs/reference/async-system.md @@ -765,7 +765,7 @@ cause a flicker on the next render. Overlapping `RunAsync` calls each get their own `callCts`; both complete in completion order. There is no built-in serialization — if the caller wants "only one in flight at a time," they gate the `RunAsync` invocation -themselves (e.g., `Button.Disabled(mut.IsPending)`). +themselves (e.g., `Button.IsEnabled(!mut.IsPending)`). ### 6.3 Unmount diff --git a/docs/specs/002-winui3-gap-analysis.md b/docs/specs/002-winui3-gap-analysis.md index 4286af12f..fa62bf64a 100644 --- a/docs/specs/002-winui3-gap-analysis.md +++ b/docs/specs/002-winui3-gap-analysis.md @@ -691,7 +691,7 @@ XY focus, and drag-drop are all missing as first-class modifiers.** | **XamlUICommand** | Missing | Label+Icon+Accelerator+Description bundling not replicated | | **StandardUICommand** | Missing | No pre-built Cut/Copy/Paste/Undo command objects | | **Command property on controls** | Missing | Controls only have OnClick/OnChanged callbacks, not a Command binding point | -| **CanExecute / auto-disable** | Missing | No automatic disable-when-unavailable; `.Disabled(condition)` is manual and must be wired separately per control | +| **CanExecute / auto-disable** | Missing | No automatic disable-when-unavailable; `.IsEnabled(!condition)` is manual and must be wired separately per control | **Verdict: Missing.** Reactor has no command abstraction. Action callbacks cover the simplest case (click → do thing) but do not replicate what ICommand provides: a named, queryable, diff --git a/docs/specs/008-csharp-language-improvements.md b/docs/specs/008-csharp-language-improvements.md index 58f322292..2b0ad85fa 100644 --- a/docs/specs/008-csharp-language-improvements.md +++ b/docs/specs/008-csharp-language-improvements.md @@ -905,7 +905,7 @@ return VStack(16) { Text("Name"), TextField(name, setName, placeholder: "Enter your name").Width(300) }, - Button("Submit", () => setSubmitted(true)).Disabled(!isValid) + Button("Submit", () => setSubmitted(true)).IsEnabled(isValid) }; ``` @@ -1879,7 +1879,7 @@ class FormDemo : Component CheckBox(agreeToTerms, setAgree, label: "I agree to the terms"), When(!isValid, () => Text("Please fill all fields and agree to terms").Opacity(0.6)), - Button("Submit", () => setSubmitted(true)).Disabled(!isValid) + Button("Submit", () => setSubmitted(true)).IsEnabled(isValid) ); } } diff --git a/docs/specs/020-async-resources-design.md b/docs/specs/020-async-resources-design.md index c1fb29ee9..ab8bad64b 100644 --- a/docs/specs/020-async-resources-design.md +++ b/docs/specs/020-async-resources-design.md @@ -552,7 +552,7 @@ var save = UseMutation( OnError: (ex, e) => cache.Revert(e.Key), InvalidateKeys: ["employees.list"])); -Button("Save", () => save.RunAsync(edited)).Disabled(save.IsPending); +Button("Save", () => save.RunAsync(edited)).IsEnabled(!save.IsPending); ``` ### 8.3 Why a separate hook, not "write support on UseResource" diff --git a/docs/specs/026-charting-accessibility-design.md b/docs/specs/026-charting-accessibility-design.md index cced21854..b298798f7 100644 --- a/docs/specs/026-charting-accessibility-design.md +++ b/docs/specs/026-charting-accessibility-design.md @@ -361,7 +361,7 @@ LineChart(...) ``` `.Interactive()` is implicit when any of `.Pan()`, `.Zoom()`, `.Brush()`, `.OnPointInvoke()`, -or `.Selectable()` is used. +or `.IsTextSelectionEnabled()` is used. --- diff --git a/docs/specs/archived/cpp-native-reconciler.md b/docs/specs/archived/cpp-native-reconciler.md index fc634f54c..768a92d19 100644 --- a/docs/specs/archived/cpp-native-reconciler.md +++ b/docs/specs/archived/cpp-native-reconciler.md @@ -812,7 +812,7 @@ class CounterDemo : Component Text($"Current count: {count}").FontSize(24).SemiBold(), HStack(8, Button($"- {step}", () => setCount(count - step)), - Button("Reset", () => setCount(0)).Disabled(count == 0), + Button("Reset", () => setCount(0)).IsEnabled(!(count == 0)), Button($"+ {step}", () => setCount(count + step)) ), HStack(8, diff --git a/docs/specs/proposals/forms-data-entry-ideas.md b/docs/specs/proposals/forms-data-entry-ideas.md index bf6670f18..d6ddf6dd4 100644 --- a/docs/specs/proposals/forms-data-entry-ideas.md +++ b/docs/specs/proposals/forms-data-entry-ideas.md @@ -351,7 +351,7 @@ ValidationVisualizer(VisualizerStyle.InfoBar, ), Button("Submit", HandleSubmit) - .Disabled(!UseValidationContext().IsValid()) + .IsEnabled(UseValidationContext().IsValid()) ) ) ``` @@ -637,9 +637,9 @@ return VStack(16, HStack(12, When(wizard.CanGoBack, () => Button("Back", wizard.GoBack)), When(!wizard.IsLastStep, () => - Button("Next", wizard.GoNext).Disabled(!wizard.IsCurrentStepValid)), + Button("Next", wizard.GoNext).IsEnabled(wizard.IsCurrentStepValid)), When(wizard.IsLastStep, () => - Button("Submit", HandleSubmit).Disabled(!wizard.IsValid)) + Button("Submit", HandleSubmit).IsEnabled(wizard.IsValid)) ) ); ``` @@ -841,7 +841,7 @@ class RegistrationForm : Component if (result.Errors is { } errors) foreach (var e in errors) ctx.Add(e.Field, e.Message); - }).Disabled(!ctx.IsValid()), + }).IsEnabled(ctx.IsValid()), When(ctx.IsDirty(), () => Button("Reset", () => ctx.Reset()) diff --git a/docs/specs/tasks/charting-accessibility-implementation.md b/docs/specs/tasks/charting-accessibility-implementation.md index f9e6f36d2..a34ad53ef 100644 --- a/docs/specs/tasks/charting-accessibility-implementation.md +++ b/docs/specs/tasks/charting-accessibility-implementation.md @@ -415,7 +415,7 @@ values, series headers, and axis labels without any visible table. - [x] Add `.Interactive()` modifier — turns on navigator (default off for static) - [x] `.Interactive()` is implicit when `.Pan()`, `.Zoom()`, `.Brush()`, - `.OnPointInvoke()`, or `.Selectable()` is used + `.OnPointInvoke()`, or `.IsTextSelectionEnabled()` is used - [x] Add `.OnPointInvoke(Action)` for Enter/Space + click - [x] Add `.OnBrushChanged(Action)` for brush selection diff --git a/plugins/reactor/skills/reactor-async/SKILL.md b/plugins/reactor/skills/reactor-async/SKILL.md index 5760cbd05..7d698d09d 100644 --- a/plugins/reactor/skills/reactor-async/SKILL.md +++ b/plugins/reactor/skills/reactor-async/SKILL.md @@ -103,7 +103,7 @@ var addTodo = UseMutation( return VStack( Button("Add", () => _ = addTodo.RunAsync(new TodoInput("New"))) - .Disabled(addTodo.IsPending)); + .IsEnabled(!addTodo.IsPending)); ``` - `OnOptimistic` runs synchronously on the caller. If it throws, the mutator diff --git a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt index e65c4a44e..babea4ade 100644 --- a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt +++ b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt @@ -209,7 +209,7 @@ BreadcrumbBarElement.ItemClicked(Action handler) → Brea BreadcrumbBarElement.Set(Action configure) → BreadcrumbBarElement ButtonElement.AccentButton() → ButtonElement ButtonElement.Click(Action handler) → ButtonElement -ButtonElement.DisabledFocusable(bool disabled = true) → ButtonElement +ButtonElement.IsDisabledFocusable(bool disabled = true) → ButtonElement ButtonElement.Set(Action