CalmUI
TypeScript

Keep a Local Toggle

Show and hide component details with a small TypeScript function.

Use local state for UI choices such as an expanded details section. Each copy of the component gets its own value. Real gameplay values belong in the Store.

Create an Empty component in Studio, replace its source, and save:

DetailsToggle.calmui
<template>
  <div>
    <button @click="toggle">Toggle details</button>
    <p ui:show="expanded">Additional information</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, state } from "@calmui/runtime";

export default defineComponent<Record<string, unknown>, unknown>(() => {
  const expanded = state(false);
  function toggle(): void {
    expanded.value = !expanded.value;
  }
  return { expanded, toggle };
});
</script>

Double-click the component to preview it. Clicking the button should show and hide the paragraph. Put two copies in a View: clicking one should not toggle the other.

state(false) creates the value. The function changes .value, and the returned names make both the value and function available to the template. ui:show hides content while keeping its component state.

Separate Views also keep separate script state. In Unreal, an inventory View and a notifications View use isolated script contexts for the same Local Player. Both receive that player's Store updates and UI clock ticks. Share gameplay data through the Store, not through script globals. Keeping a View alive retains its context; destroying it releases its component state and timers.

Use computed for a value calculated from other values and watch when a change needs a side effect. A watch returns a stop function; call it when you no longer need to observe changes, or from onUnmount when the component is removed.

Anything the template shows must be state, computed, or props. The View re-renders a component when a reactive value its template read changes, and only then: a handler that only arms a timer or dispatches an action does not re-render anything. A plain variable changed in place (a module-level object, an array pushed into without assigning .value) is not observed, so the template keeps showing the old value until something reactive changes.

Props also participate in these calculations. Read props.label inside a computed callback to update a caption when its parent changes the label. Props, including nested objects, are read-only; use state for local edits and emit an event when the owner should accept a new value. See Pass Values to Components.

Events without a click

Component events emitted from timers or prop watchers reach parent listeners at the next view update, without requiring a click or pointer movement. A parent can update local state in that listener; the view settles those changes before rendering. Cyclic event/watch feedback is rejected after 1,024 emissions in one update. A timer, action, emission, or diagnostic created inside a Store-driven watcher remains owned by that component instance. Keep timer cleanup in onUnmount so removed components cannot finish old work later.

On this page