@everystate/view-ui
v1.0.0
Published
Declarative UI component specs for EveryState: plain JS objects + handler maps + default state shapes
Downloads
93
Maintainers
Readme
@everystate/view-ui
Declarative UI component specs for EveryState: plain JS objects + handler maps + default state shapes.
The declarative twin of @everystate/ui. While @everystate/ui provides imperative mount(store, root) -> teardown components, this package provides view specs that work with @everystate/view's flatten + mount pipeline.
Architecture: No Ceiling
@everystate/view-ui @everystate/ui
(declarative specs) (imperative components)
\ /
------> composed via <-----
{ component: "name" }
in declarative specsThe { component } escape hatch in @everystate/view lets declarative specs embed imperative components as leaf nodes. The store's dot-paths are the universal interface: neither side needs to know about the other.
Installation
npm install @everystate/view-uiPeer dependencies: @everystate/core, @everystate/view
Quick Start
import { flatten } from '@everystate/view/resolve';
import { mount } from '@everystate/view/project';
import { tableSpec, tableHandlers, tableDefaults } from '@everystate/view-ui/table';
// Initialize store with defaults
const store = createEveryState(tableDefaults);
// Flatten spec -> store, mount -> DOM
flatten(tableSpec, store, 'view.table');
const cleanup = mount(store, 'view.table', document.getElementById('root'), tableHandlers(store));Components
Each component exports three things:
| Export | Type | Description |
|--------|------|-------------|
| *Spec | Plain JS object | The view specification: inspectable, editable, composable |
| *Handlers(store) | Function: handler map | Event handlers for onClick/onEnter/onBlur |
| *Defaults | Plain JS object | Initial store shape, documents the contract |
Available Components
| Component | Deep Import | Patterns Used |
|-----------|------------|---------------|
| Table | @everystate/view-ui/table | Nested forEach, fragment forEach, bind interpolation |
| Counters | @everystate/view-ui/counters | forEach over TOC, bind for non-input display |
| Theme | @everystate/view-ui/theme | Simple button group, data-theme on html |
| Accordion | @everystate/view-ui/accordion | forEach, bind, toggle handlers |
| Tabs | @everystate/view-ui/tabs | forEach, bind, exclusive visibility |
| Form | @everystate/view-ui/form | bind for inputs, validation, error display |
The Escape Hatch: { component }
Mix declarative specs with imperative mount functions:
import { countersSpec } from '@everystate/view-ui/counters';
import { mountFSM } from '@everystate/ui/fsm';
const pageSpec = {
tag: "div", children: [
countersSpec, // <- declarative
{ component: "fsm" }, // <- imperative escape hatch
]
};
const components = { fsm: mountFSM };
const handlers = countersHandlers(store);
flatten(pageSpec, store, 'view.page');
mount(store, 'view.page', el, handlers, components);Forking & Composing
Every spec is a plain JS object. Fork it:
import { tableSpec } from '@everystate/view-ui/table';
// Add a title
const myTable = {
tag: "div", children: [
{ tag: "h2", text: "My Custom Table" },
tableSpec,
]
};Or modify it. More description will be added here in the future.
Store Shape = Component Contract
Each component's interface is the set of store paths it reads and writes:
Table: columnIds, teamIds, team.*, ui.newColumnName
Counters: counterIds, counters.*
Theme: theme
Accordion: accordion.sectionIds, accordion.sections.*, accordion.open.*
Tabs: tabs.main.active, tabs.main.tabIds, tabs.main.panels.*
Form: form.fields.*, form.errors.*, form.valid, form.submittedSelf-Test
node node_modules/@everystate/view-ui/self-test.js
# or
npx everystate-view-ui-self-testPhilosophy
- Transparent: every spec is a readable plain object
- No ceiling:
{ component }lets you escape to imperative when needed - No floor: beginners write specs, experts fork them
- Zero dependencies: just peer deps on core + view
- Longevity: plain JS objects never break
License
MIT © Ajdin Imsirovic
