sus-core vs Vue 3 - audit and improvement plan
Date: June 30, 2026
Status: primary audit completed, tasks assigned⚠️ Historical planning doc — several "to-fix" items below are already DONE. Corrections:
- §6.1
SusThemeService.Current(reactiveProp<SusTheme>) exists —Watch(SusThemeService.Current, …)works.- §6.2
SusResolutionServiceremoved — screen adaptation is breakpoints only (SusBreakpointService).- Router KeepAlive is an off-DOM cache (
SusScreenOutletdetaches + caches the screen), notdisplay:noneand not a coreSusKeepAlivewrapper. Seesus-router/docs/04-routeview.md. Thedisplay:none/SusKeepAlivementions below are stale.
Full audit of all 19 Runtime files of sus-core and compiler. Comparison with Vue 3 for each subsystem: what is there, what is not, what to fix.
Content
5b. Unity 6+ UITK - unused native features 6. Technical debt (bugs/inconsistencies) 7. Consolidated priority list 8. App: Vue pivot table → sus-core
Legend
| Marker | Meaning |
|---|---|
| 🔥 | Critical - blocks development of each screen/form |
| ⚠️ | Important - simplifies development, removes boilerplate |
| 🔧 | Technical debt - fix in current code |
| 📦 | The future is not a blocker, architectural improvements |
| ✅ | Already implemented |
1. Reactivity
1.1 ✅ Prop<T> - analogue of Vue ref<T>
Status: implemented.
File: sus-core/Runtime/Prop.cs
Reactive property with Changed event, Unity data binding, auto-tracking from Computed<T> through DependencyTracker. Implicit operator for reading without .Value.
1.2 ✅ Computed<T> - analogue of Vue computed<T>
Status: implemented (July 1, 2026 - added IReactiveSource).
Files: sus-core/Runtime/Computed.cs, DependencyTracker.cs, IReactiveSource.cs
Auto tracking via [ThreadStatic] collector. On the first reading, executes fn, subscribes to all read Prop<T> / Computed<T>, caches. When changing any dependency - MarkDirty() (only at the front false→true), the next reading recalculates.
Important: from July 1 Computed<T> himself is IReactiveSource:
ValuecausesDependencyTracker.RegisterSource(this)— external tracking sees computed as a sourceMarkDirty()forwards invalidation to its subscribers (push up the chain)- Chains
Prop → Computed A → Computed B → BindTextwork
1.3 ✅ Watch(Prop, callback) - analogue of Vue watch(ref, fn)
Status: implemented.
Files: sus-core/Runtime/SusComponent.cs:26-33, WatchHandle.cs
Single Prop watcher. Returns WatchHandle : IDisposable — when Dispose unsubscribes.
1.4 ✅ watchEffect(fn) - analogue of Vue watchEffect
Status: ✅ IMPLEMENTED.
Vue equivalent: watchEffect(() => { ... })
WatchEffect(Action fn) on SusComponent auto-tracks all Prop / Computed reads inside fn and re-runs when any dependency changes. Returns WatchHandle for dispose.
WatchEffect(() => {
var total = hp.Value + maxHp.Value;
var label = name.Value;
UpdateUI(total, label); // auto-restart when hp, maxHp or name changes
});See sus-core/Runtime/SusComponent.cs and 03-reactivity.md.
1.5 ⚠️ Deep reactivity - similar to Vue reactive(obj)
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: const state = reactive({ hp: 100, name: "Unit" })
Problem
Prop<T> - only one meaning. For an object with 5 fields, you need 5 separate ones Prop<T>. If the field changes directly ( unit.HP = 50), UI is not updated - there is no property-change tracking mechanism.
What to do
Option A (minimal): Document the pattern in README/ARCHITECTURE:
// Each mutable field → separate Prop<T>
public Prop<int> HP = new(100);
public Prop<int> MaxHP = new(100);
public Prop<string> Name = new("Unit");Option B (full-fledged, 📦 future): Reactive<T> where T : INotifyPropertyChanged - a wrapper that listens PropertyChanged and disables Computed<T>. Useful for ViewModel pattern.
Priority: ⚠️ (document now, Reactive - 📦)
2. Component model
2.1 ✅ Emit / On / Off - analogue of Vue defineEmits
Status: implemented.
Files: sus-core/Runtime/SusComponent.Events.cs
Typed events: Emit<TEvent>(string name, TEvent data), On<TEvent>(name, handler), Off<TEvent>(name, handler). Events are bubbled through the UI Toolkit EventBase.
2.2 ✅ BuildSlot / RegisterSlotContent - analogue of Vue <slot>
Status: implemented.
Files: sus-core/Runtime/SusComponent.Slots.cs, SlotProps.cs
Named slots + scoped props (SlotPropMap) + content registration ( v-slot).
2.2b ✅ Props between components - analogous to Vue prop-passing
Status: implemented (2026-07-01).
Files: sus-core/Runtime/SusComponent.Bind.cs, BuildMethodGenerator.cs
- Literal props (
variant="primary") —SetChildPropwith IgnoreCase, mutates.ValueexistingProp<T>(does not replace the instance → preserves the child’s internal bindings). - Reactive props (
:variant="expr") —BindChildProp(runtime helper with IgnoreCase, symmetricSetChildProp). Generator issuesBindChildProp(child, "propName", () => expr). - Scalar conversion:
string → bool/int/float/enum/stringthroughConvertScalar(enum viaEnum.Parse). - Diagnostics:
LogWarning/LogErrorin the dev build in case of conversion errors.
2.3 ✅ Lifecycle: 4 hooks
Status: implemented (partially).
File: sus-core/Runtime/SusComponent.Lifecycle.cs
| Hook Sus | Vue Hook | Status |
|---|---|---|
Created() | setup() /constructor | ✅ |
Mounted() | onMounted() | ✅ |
Updated() | onUpdated() | ✅ (each frame) |
Unmounted() | onUnmounted() | ✅ |
2.4 🔥 Provide / Inject - analogue of Vue provide / inject
Status: ❌ NOT IMPLEMENTED.
Vue equivalent:
// Parent:
provide('theme', themeRef)
// Child (any depth):
const theme = inject('theme')Problem
Without Provide/Inject - prop drilling through the entire hierarchy. Theme, localization, game state, router - everything needs to be dragged through each intermediate component.
// Now - antipattern:
<sus:App>
<sus:MainMenu theme={theme} locale={locale} /> // ← drag theme
<sus:SettingsPanel theme={theme} /> // ← drag again
<sus:ThemeToggle theme={theme} /> // ← and againWhat to do
- Add to
SusComponent:
// Provide value - visible to all children
protected void Provide<T>(string key, T value);
// Get value - searches up the hierarchy
protected T Inject<T>(string key);
// Check availability
protected bool HasInjection(string key);- Storage -
Dictionary<string, object>onSusComponent. Injectlooks for itself → parent → parent.parent → ... until it finds or root.- For reactive values - put
Prop<T>, the consumer subscribes.
Files:
sus-core/Runtime/SusComponent.cs- methodsProvide/Injectsus-core/Runtime/SusComponent.Inject.cs- implementation in a separate partialsus-core/Docs/11-api-reference.md— Provide/Inject section
Priority: 🔥
2.5 ⚠️ BeforeMounted() / BeforeUnmounted()
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: onBeforeMount(), onBeforeUnmount()
Problem
Unmounted() called in OnDetachFromPanelHandler after detach. In Vue beforeUnmount - to to clear resources while the DOM is still alive. Both are needed:
// Desired order:
// 1. BeforeMounted() - before Build(), but after the constructor
// 2. Build() + LoadCompanionStyleSheets()
// 3. Mounted() - after attaching to the panel
// ...
// 4. BeforeUnmounted() - BEFORE removal from the panel (clearing subscriptions, timers)
// 5. Unmounted() - AFTER removal (final cleanup)What to do
- Add
virtual void BeforeMounted()- called in the constructor betweenCreated()AndBuild(). - Add
virtual void BeforeUnmounted()- called inOnDetachFromPanelHandlerto_updateItem.Pause().
File: sus-core/Runtime/SusComponent.Lifecycle.cs
Priority: ⚠️
2.6 📦 Declarative props — defineProps with default/validate
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: defineProps({ title: { type: String, default: "Untitled" } })
Problem
Fields in <script> - just C# fields. There is no difference between “this is a prop” and “this is an internal state”. There are no default values through the attribute, no validation.
What to do (future)
Add attribute [Prop(default: ..., validator: ...)] or [UxmlAttribute] for generation. Now - document the current approach (public fields = props).
Priority: 📦
3. Template directives
3.1 ✅ v-if, v-show, v-for, :text, @click - reactive bindings
Status: implemented (July 1, 2026 - bindings transferred to ReactiveEffect).
Files: SusComponent.Bind.cs, BuildMethodGenerator.cs, TemplateParser.cs
From July 1st everything Bind* methods ( BindText, BindShow, BindVisibility, BindClass, BindList, BindListFor) work through a common reactive primitive ReactiveEffect:
- Each binding is performed under
DependencyTracker.Track— auto-collection of dependencies (Prop/Computed) - Subscription to invalidation - changing any Prop/Computed causes the binding to be reapplied
- Batching through
ScheduleBindUpdate(one pass per frame), deduplication viaHashSet<Action> - Auto-clearing of subscriptions when detaching a component (
DisposeAllBindings)
3.2 🔥 v-model - two-way binding
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: <input v-model="text" />
Problem
The most common pattern in forms. Now:
// Manual subscription - boilerplate for each TextField:
var field = new TextField();
field.value = name.Value;
field.RegisterCallback<ChangeEvent<string>>(evt => name.Value = evt.newValue);What to do
- Add
BindModelVSusComponent.Bind.cs:
// Two-way binding for TextField, Slider, Toggle, DropdownField
protected void BindModel(TextField field, Prop<string> prop);
protected void BindModel(Slider slider, Prop<float> prop);
protected void BindModel(Toggle toggle, Prop<bool> prop);
protected void BindModel(DropdownField dropdown, Prop<string> prop);- In the compiler: parse
v-model="propName"→ generateBindModel(el, propName).
Files:
sus-core/Runtime/SusComponent.Bind.cs- new methodsBindModelsus-core/Editor/SourceGenerator/Parser/TemplateParser.cs- parsingv-modelsus-core/Editor/SourceGenerator/Generator/BuildMethodGenerator.cs- generationBindModel
Priority: 🔥
3.3 🔥 v-else-if / v-else
Status: ❌ NOT IMPLEMENTED.
Vue equivalent:
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>Problem
Now we have to duplicate the condition with inversion:
<!-- Antipattern - duplication of logic: -->
<ui:VisualElement v-if="State == 'A'" />
<ui:VisualElement v-if="State != 'A' && State == 'B'" />
<ui:VisualElement v-if="State != 'A' && State != 'B'" />WITH v-else-if/ v-else - a clean chain, the compiler will construct the conditions itself.
What to do
TemplateParseris already building a flat tree of siblings. When foundv-else-if/v-elseon sibling → link to previousv-ifto the group.BuildMethodGeneratorgenerates a chainif/else if/else.
Files:
sus-core/Editor/SourceGenerator/Parser/TemplateParser.cs— grouping v-if/v-else-if/v-elsesus-core/Editor/SourceGenerator/Generator/BuildMethodGenerator.cs— chain generation
Priority: 🔥
3.4 ⚠️ :class object syntax
Status: ❌ Single BindClass only.
Vue equivalent: :class="{ active: isActive, error: hasError }"
Problem
Now :class="'my-class'" - line. For several conditional classes - manual addition:
<!-- The current approach is 2 separate directives (not supported by the compiler): -->
<ui:VisualElement :class="'active'" :class="'error'" />What to do
TemplateParserparses:class="{ active: isActive, error: hasError }"→ list of pairs (className, condition).BuildMethodGeneratorgenerates severalBindClass(el, "active", () => isActive),BindClass(el, "error", () => hasError).
Files: the same compiler ones as for directives.
Priority: ⚠️
3.5 ⚠️ Event modifiers: .stop, .prevent, .once
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: @click.stop="handler", @click.once="handler"
What to do
TemplateParserparses@click.stop="handler"→ flagsstopPropagation,oncein the AST node.BuildMethodGeneratorgenerates a wrapper:
// @click.stop="handler" →
el.RegisterCallback<ClickEvent>(evt => {
evt.StopPropagation();
handler();
});
// @click.once="handler" →
bool _once = false;
el.RegisterCallback<ClickEvent>(evt => {
if (!_once) { _once = true; handler(); }
});Files: TemplateParser.cs, BuildMethodGenerator.cs
Priority: ⚠️
4. Built-in components
4.1 📦 KeepAlive - DOM tree caching
Status: ❌ NOT IMPLEMENTED.
Vue equivalent: <KeepAlive><component :is="view" /></KeepAlive>
Relevant for sus-router. Push/Replace navigation hides the screen (display: none) instead of being removed from the DOM. Mounted() is not called again, the Prop state is preserved.
Priority: 📦
4.2 📦 Transition / TransitionGroup - animations
Status: ❌ NOT IMPLEMENTED.
UITK supports animations via experimental.animation. You can wrap fade/slide in a SusTransition component.
Priority: 📦
5. Application infrastructure
5.1 SusApp — analog of createApp().use().mount()
Status: ✅ Implemented (Sharq.Core.SusApp).
Vue equivalent: const app = createApp(App).use(router).use(pinia).mount('#app')
Fluent bootstrap: TSS → tokens → configure/router → mount → theme. Documented entry point; ensures OverlayHost. See 00-integration.md (Critical + Step 6).
SusApp.Create(uiDocument)
.UseTheme(SusTheme.Dark)
.Mount<App>();Older notes below about “NOT IMPLEMENTED” are obsolete — keep this section as the source of truth.
5.2 ✅ Devtools - SusComponent inspector
Status: ✅ IMPLEMENTED (SusDevtools).
Attach with SusDevtools.Attach(root) — inspects the SusComponent tree, Prop values, and allows runtime edits. Complements UI Toolkit Debugger (which only shows VisualElements).
5b. Unity 6+ UITK - unused native features
Date: June 30, 2026 - separate audit
Conclusion: the project practically does not use Unity 6+ UI Toolkit features. Below is what Unity provides natively and how it can be replaced or supplemented in sus-core.
Tested features (all NOT USED)
| # | Unity 6+ feature | Where is it used | Project status |
|---|---|---|---|
| 1 | dataSource on VisualElement | Native data-binding: el.dataSource = vm | ❌ Nowhere |
| 2 | DataBinding / SetBinding() | el.SetBinding("text", new DataBinding(...)) | ❌ → ✅ BindModelNative |
| 3 | BindingMode.ToTarget / .ToSource / .TwoWay | Two-way binding without code | ❌ → ✅ BindModelNative |
| 4 | PropertyPath | PropertyPath.FromName("Unit.Name") - nested properties | ❌ Nowhere |
| 5 | [UxmlAttribute] | Expose properties in UI Builder | ✅ Auto-generated companion property from [CreateProperty] |
| 6 | CustomBinding | Custom binding types | ❌ Nowhere |
| 7 | USS :hover | Native hover: .btn:hover { ... } | ❌ Everything via C# MouseEnterEvent |
| 8 | USS :focus | Focus: .input:focus { ... } | ❌ Nowhere |
| 9 | USS :enabled / :disabled | States: .btn:disabled { opacity: 0.5; } | ❌ Nowhere |
| 10 | USS transition | transition: background-color 0.2s ease; | ❌ Nowhere (experimental in Unity 6) |
| 11 | TwoPaneSplitView | Built-in split control | ❌ Nowhere |
| 12 | MultiColumnListView / MultiColumnTreeView | Tables with sorting | ❌ Nowhere |
| 13 | RuntimePanelUtils | Utilities: ScreenToPanel, coordinates | ❌ Nowhere |
| 14 | PanelSettings.themeStyleSheet | Topic via .tss (we replaced it with root.styleSheets.Add()) | ❌ Consciously refused |
5b.1 🔥 USS :hover / :focus / :disabled - replace C# handlers
Problem
Now hover and focus are done through C#:
el.RegisterCallback<MouseEnterEvent>(OnHoverEnter);
el.RegisterCallback<MouseLeaveEvent>(OnHoverLeave);
// → AddToClassList("hovered") / RemoveFromClassList("hovered")This is a boilerplate for each interactive element. Unity USS supports :hover from 2022.2 - you can remove 90% of C# handlers.
What to do
/* Instead of C# - pure USS: */
.sus-button {
background-color: var(--sus-btn-primary-bg);
transition: background-color 0.15s ease; /* experimental in Unity 6 */
}
.sus-button:hover {
background-color: var(--sus-btn-primary-bg-hover);
}
.sus-button:disabled {
opacity: 0.4;
}
.sus-input:focus {
border-color: var(--sus-input-border-focus);
}Effect: delete MouseEnterEvent/ MouseLeaveEvent from each component, where the hover is purely visual (color/background change). C# remains only for logic (tooltips, previews).
Priority: 🔥 - gives instant winnings on all interactive components
5b.2 ⚠️ Native data binding vs our BindText/BindShow
What does Unity provide?
// Unity native:
var label = new Label();
label.dataSource = myViewModel;
label.SetBinding("text", new DataBinding {
bindingMode = BindingMode.ToTarget,
dataSourcePath = PropertyPath.FromName("UnitName")
});
// Our current approach:
BindText(label, () => unitName.Value);Comparison
| Criterion | Unity native binding | Our BindText/BindShow |
|---|---|---|
| Code | More boilerplate | In short (1 line) |
| Double-sided | BindingMode.TwoWay - out of the box | Need manual BindModel |
INotifyBindablePropertyChanged | ✅ Prop is ALREADY implementing | ✅ Prop is ALREADY implementing |
| UI Builder | ✅ Sees bindings | ❌ Doesn't see |
| Auto-track dependencies | ❌ No (property change only) | ✅ Computed + DependencyTracker |
watchEffect | ❌ Incompatible | ✅ Planned |
computed | ❌ Incompatible | ✅ Implemented |
Solution: DO NOT replace, but supplement
Unity native binding can't track dependencies (no computed, No watchEffect) is a fundamental limitation. Our jet system ( Prop<T> + Computed<T>) remains the core.
What to do: add optional BindNative() for cases where computed is not needed:
// Fast way (Unity native) - for simple props:
el.SetBinding("text", new DataBinding {
dataSource = myProp, // Prop<T> : INotifyBindablePropertyChanged
dataSourcePath = PropertyPath.FromName("Value"),
bindingMode = BindingMode.TwoWay // ← v-model out of the box!
});
// Slow way (ours) - for computed/watchEffect/complex logic:
BindText(el, () => computedValue.Value);This gives v-model for free via BindingMode.TwoWay.
Priority: ⚠️
5b.3 🔥 [UxmlAttribute] — visibility of props in UI Builder
Problem
SusComponent already has [UxmlElement] (auto-generated). But props ( Prop<T>) are not visible in UI Builder because there are no [UxmlAttribute].
What to do
IN Prop<T> add attribute [UxmlAttribute] for Value (or a separate property):
[UxmlElement]
public partial class SusButton : SusComponent
{
[UxmlAttribute("text")]
public Prop<string> Text { get; set; } = new("Button");
}This will allow you to change props directly in UI Builder without code.
Priority: 🔥 - Unlocks UI Builder for designers
5b.4 Summary: what to implement from Unity 6+
| # | Feature | Priority | What will give |
|---|---|---|---|
| 1 | USS :hover / :focus / :disabled | 🔥 | Remove C# hover handlers from all components |
| 2 | [UxmlAttribute] on Prop | 🔥 | Visibility of props in UI Builder |
| 3 | USS transition (experimental) | ⚠️ | Smooth hover/focus animations on USS |
| 4 | SetBinding + BindingMode.TwoWay | ✅ BindModelNative | v-model out of the box for simple props |
| 5 | TwoPaneSplitView and other controls | 📦 | Fewer controls |
| 6 | RuntimePanelUtils.ScreenToPanel | 📦 | Correct coordinates for OverlayHost |
DO NOT implement (fundamentally incompatible with our reactivity):
- Complete transition to Unity native binding - no computed/watchEffect/tracking
PanelSettings.themeStyleSheet- has already been replaced byroot.styleSheets.Add()
6. Technical debt (bugs/inconsistencies)
6.1 🔧 SusThemeService - no reactive Current
File: sus-core/Runtime/SusThemeService.cs
Problem: SetTheme changes CSS classes, but components cannot subscribe to the theme change. No Prop<SusTheme>.
Correction:
public class SusThemeService
{
public static readonly SusThemeService Instance = new();
public static Prop<SusTheme> Current { get; } = new(SusTheme.Dark);
private SusThemeService() { }
public void SetTheme(VisualElement root, SusTheme theme)
{
Current.Value = theme;
// ... existing class toggle logic ...
}
}Now any component can Watch(SusThemeService.Current, ...).
6.2 SusResolutionService — removed
Status: deleted. Do not reintroduce High/Low resolution classes. Screen-size adaptation is only SusBreakpointService + .breakpoint-* token overrides.
The former resolution feed path is preserved on breakpoints: every component GeometryChanged pushes cascadeRoot.resolvedStyle.width onto SusBreakpointService.Attach(cascadeRoot).Update(width). See 06-responsive.md.
6.3 🔧 dismissOnClickOutside in OverlayHost - not implemented
File: sus-core/Runtime/OverlayHost.cs:29
Problem: Parameter dismissOnClickOutside is accepted, but the click-out is not tracked.
Correction:
- Register
ClickEventon the root panel. - When clicked, check whether the target of the click is inside the overlay element? If not → call
OnDismissand delete. - Register/cancel only when there is at least one overlay with
dismissOnClickOutside: true.
6.4 🔧 HasSlotContent() - no slot check from code
File: sus-core/Runtime/SusComponent.Slots.cs
Problem: Cannot be verified HasSlot("header") from C#. Vue: useSlots().header.
Fix: Add method:
protected bool HasSlotContent(string slotName) =>
_registeredSlots.ContainsKey(slotName);6.5 🔧 SusBreakpointService - redundant per-component instance
Files: sus-core/Runtime/SusComponent.cs:129, SusBreakpointService.cs
Problem: Each component creates its own SusBreakpointService. For 200 components - 200 services that duplicate the same breakpoint.
Correction: Make shared to root (like SusThemeService), or at least a singleton on panel.visualTree:
// Option: cache on rootVisualElement via userData
var bp = root.userData as SusBreakpointService;
if (bp == null) {
bp = new SusBreakpointService();
root.userData = bp;
}7. Consolidated priority list
🔥 Critical (block development)
- [x] 1.
v-model- two-way binding forTextField,Slider,Toggle,DropdownField. Affects:SusComponent.Bind.cs,TemplateParser.cs,BuildMethodGenerator.cs. - [x] 2. Provide / Inject — remove prop drilling. Topic, locale, game state - available at any depth without transmission through the chain. Affects:
SusComponent.cs(new partial),SusComponent.Inject.cs. - [x] 3.
v-else-if/v-else- pure conditional chains without duplicating conditions. Affects:TemplateParser.cs,BuildMethodGenerator.cs. - [ ] 4. USS
:hover/:focus/:disabled- replace C#MouseEnterEvent/MouseLeaveEventto native USS pseudo-classes. Remove boilerplate from each interactive component. Affects: everything.uss, all components. - [x] 5.
[UxmlAttribute]on Prop** - visibility of props in UI Builder. Affects: **Prop.cs, auto-generation.
⚠️ Important (makes development easier)
- [x] 6.
watchEffect(fn)- less boilerplate: one subscription to several Props instead of N separate Watches. Affects:SusComponent.cs,Docs/03-reactivity.md. - [x] 7.
:classobject syntax - several conditional classes on one element. Affects:TemplateParser.cs,BuildMethodGenerator.cs. - [x] 8. Event modifiers (
.stop,.once) - frequent patterns in handlers. Affects:TemplateParser.cs,BuildMethodGenerator.cs. - [x] 9.
BeforeMounted()/BeforeUnmounted()is the correct order for clearing resources. Affects:SusComponent.Lifecycle.cs,SusComponent.cs. - [x] 10.
SetBinding+BindingMode.TwoWay- v-model for simple props via Unity native binding. Affects:SusComponent.Bind.cs, optional.
🔧 Technical debt (fix in current code)
- [x] 11.
SusThemeService- addProp<SusTheme> Current(topic reactivity). File:SusThemeService.cs. - [x] 12.
SusResolutionService— removed; use breakpoints only. Files: deletedSusResolutionService.cs; geometry hook keepsSusBreakpointServiceonly. - [x] 13.
dismissOnClickOutside- implement logic inOverlayHost. File:OverlayHost.cs. - [x] 14.
HasSlotContent(string name)- slot check from C#. File:SusComponent.Slots.cs. - [x] 15.
SusBreakpointService- make it generic for root instead of per-component. Files:SusComponent.cs,SusBreakpointService.cs.
📦 Future (not blockers)
[x] 16.
KeepAlive- caching screens when navigating via SusKeepAlive (sus-core) + keepAlive:true in SusRouteConfig (sus-router).[x] 17.
Transition— animation system (Fade, SlideLeft, SlideRight) in SusRouteTransition (sus-router).[x] 18. Declarative props —
definePropswith default/validate.[x] 19.
SusApp—SusApp.Create().UseTheme().UseRouter().Mount<T>()/Run()(see 00-integration Critical).[x] 20. Devtools-panel - inspection of SusComponent, Prop values, tree (F12-panel).
[x] 21. Deep reactivity —
Reactive<T>for ViewModel (now - document the “every field = Prop” pattern).
Application: Vue pivot table → sus-core
| Vue Feature | sus-core equivalent | Status |
|---|---|---|
ref() | Prop<T> | ✅ |
computed() | Computed<T> + DependencyTracker | ✅ |
watch() | Watch(prop, callback) → WatchHandle | ✅ |
watchEffect() | WatchEffect(Action fn) | ✅ |
reactive(obj) | "Each field = Prop" (pattern documented) | ✅ |
defineProps | [CreateProperty(default: X, validate: "...")] | ✅ |
defineEmits | Emit<TEvent>() / On<TEvent>() | ✅ |
provide / inject | Provide<T> / Inject<T> | ✅ |
| Lifecycle (8 hooks) | 6 hooks (Created/BeforeMounted/Mounted/Updated/BeforeUnmounted/Unmounted) | ⚠️ |
defineExpose | — (C# reference) | ⚠️ |
v-model | BindModel (manual two-way, without PropertyPath) | ✅ |
v-if / v-else-if / v-else | v-if / v-else-if / v-else | ✅ |
v-show | BindShow | ✅ |
v-for + :key | BindList<T> / BindListFor<T> | ✅ |
:text | BindText | ✅ |
:class (object) | :class="{ cls: cond }" → BindClass | ✅ |
@click / @event | RegisterCallback | ✅ |
Event modifiers (.stop/.once) | @click.stop / @click.once | ✅ |
v-once / v-memo | — | ❌ |
<Teleport> | OverlayHost ( AddToOverlay / RemoveFromOverlay) | ✅ |
<Transition> / <TransitionGroup> | SusRouteTransition (Fade/SlideLeft/SlideRight) | ✅ |
<KeepAlive> | SusKeepAlive + SusRouteConfig.KeepAlive | ✅ |
<Suspense> | — | ❌ |
| Async components | — | ❌ |
app.use(plugin) | — | ❌ |
app.config | sus.config.json (compile-time) | ⚠️ |
Composables (use*()) | — (static helpers / DI) | ⚠️ |
| Devtools | SusDevtools (F12 - tree + Prop editor) | ✅ |
Unity 6+ features (not Vue, but important)
| Unity feature | Status | Priority |
|---|---|---|
USS :hover / :focus / :disabled | ❌ Everything through C# events | 🔥 |
USS transition (experimental) | ❌ Not used | ⚠️ |
[UxmlAttribute] | ✅ Comanion property is generated by auto | 🔥 |
dataSource + SetBinding + DataBinding | BindModelNative (optional, for simple props) | ✅ |
BindingMode.TwoWay (v-model native) | BindModelNative — SetBinding c TwoWay | ✅ |
Unity TwoPaneSplitView | ❌ Not used | 📦 |
RuntimePanelUtils | ❌ Not used | 📦 |