@elaraai/e3-ui 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/src/data.d.ts +22 -1
- package/dist/src/data.d.ts.map +1 -1
- package/dist/src/data.js.map +1 -1
- package/dist/src/decision/bind.d.ts +4594 -0
- package/dist/src/decision/bind.d.ts.map +1 -0
- package/dist/src/decision/bind.js +156 -0
- package/dist/src/decision/bind.js.map +1 -0
- package/dist/src/decision/index.d.ts +1787 -0
- package/dist/src/decision/index.d.ts.map +1 -0
- package/dist/src/decision/index.js +36 -0
- package/dist/src/decision/index.js.map +1 -0
- package/dist/src/decision/journal.d.ts +405 -0
- package/dist/src/decision/journal.d.ts.map +1 -0
- package/dist/src/decision/journal.js +74 -0
- package/dist/src/decision/journal.js.map +1 -0
- package/dist/src/decision/queue.d.ts +2876 -0
- package/dist/src/decision/queue.d.ts.map +1 -0
- package/dist/src/decision/queue.js +166 -0
- package/dist/src/decision/queue.js.map +1 -0
- package/dist/src/decision/types.d.ts +1338 -0
- package/dist/src/decision/types.d.ts.map +1 -0
- package/dist/src/decision/types.js +389 -0
- package/dist/src/decision/types.js.map +1 -0
- package/dist/src/diff.d.ts +72 -0
- package/dist/src/diff.d.ts.map +1 -1
- package/dist/src/diff.js +6 -0
- package/dist/src/diff.js.map +1 -1
- package/dist/src/index.d.ts +27 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +30 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +28 -0
- package/dist/src/internal.d.ts.map +1 -0
- package/dist/src/internal.js +28 -0
- package/dist/src/internal.js.map +1 -0
- package/dist/src/ontology.d.ts +205 -10
- package/dist/src/ontology.d.ts.map +1 -1
- package/dist/src/ontology.js +23 -4
- package/dist/src/ontology.js.map +1 -1
- package/dist/src/runtime/decision/journal.d.ts +29 -0
- package/dist/src/runtime/decision/journal.d.ts.map +1 -0
- package/dist/src/runtime/decision/journal.js +27 -0
- package/dist/src/runtime/decision/journal.js.map +1 -0
- package/dist/src/runtime/decision/queue.d.ts +47 -0
- package/dist/src/runtime/decision/queue.d.ts.map +1 -0
- package/dist/src/runtime/decision/queue.js +45 -0
- package/dist/src/runtime/decision/queue.js.map +1 -0
- package/dist/src/runtime/diff.d.ts +36 -0
- package/dist/src/runtime/diff.d.ts.map +1 -0
- package/dist/src/runtime/diff.js +34 -0
- package/dist/src/runtime/diff.js.map +1 -0
- package/dist/src/runtime/index.d.ts +15 -0
- package/dist/src/runtime/index.d.ts.map +1 -0
- package/dist/src/runtime/index.js +15 -0
- package/dist/src/runtime/index.js.map +1 -0
- package/dist/src/runtime/ontology.d.ts +36 -0
- package/dist/src/runtime/ontology.d.ts.map +1 -0
- package/dist/src/runtime/ontology.js +34 -0
- package/dist/src/runtime/ontology.js.map +1 -0
- package/dist/src/runtime/runtime.d.ts +7 -0
- package/dist/src/runtime/runtime.d.ts.map +1 -0
- package/dist/src/runtime/runtime.js +7 -0
- package/dist/src/runtime/runtime.js.map +1 -0
- package/dist/src/ui.d.ts +6 -6
- package/dist/src/ui.d.ts.map +1 -1
- package/dist/src/ui.js +9 -1
- package/dist/src/ui.js.map +1 -1
- package/dist/test/{data.examples.d.ts → data/data.examples.d.ts} +1 -0
- package/dist/test/data/data.examples.d.ts.map +1 -0
- package/dist/test/{data.examples.js → data/data.examples.js} +30 -68
- package/dist/test/data/data.examples.js.map +1 -0
- package/dist/test/decision/journal.examples.d.ts +266 -0
- package/dist/test/decision/journal.examples.d.ts.map +1 -0
- package/dist/test/decision/journal.examples.js +107 -0
- package/dist/test/decision/journal.examples.js.map +1 -0
- package/dist/test/decision/loop.examples.d.ts +455 -0
- package/dist/test/decision/loop.examples.d.ts.map +1 -0
- package/dist/test/decision/loop.examples.js +120 -0
- package/dist/test/decision/loop.examples.js.map +1 -0
- package/dist/test/decision/queue.examples.d.ts +269 -0
- package/dist/test/decision/queue.examples.d.ts.map +1 -0
- package/dist/test/decision/queue.examples.js +273 -0
- package/dist/test/decision/queue.examples.js.map +1 -0
- package/dist/test/{diff.examples.d.ts → diff/diff.examples.d.ts} +5 -4
- package/dist/test/diff/diff.examples.d.ts.map +1 -0
- package/dist/test/diff/diff.examples.js +582 -0
- package/dist/test/diff/diff.examples.js.map +1 -0
- package/dist/test/{ontology.examples.d.ts → ontology/ontology.examples.d.ts} +6 -4
- package/dist/test/ontology/ontology.examples.d.ts.map +1 -0
- package/dist/test/{ontology.examples.js → ontology/ontology.examples.js} +37 -57
- package/dist/test/ontology/ontology.examples.js.map +1 -0
- package/package.json +19 -19
- package/dist/test/data.examples.d.ts.map +0 -1
- package/dist/test/data.examples.js.map +0 -1
- package/dist/test/diff.examples.d.ts.map +0 -1
- package/dist/test/diff.examples.js +0 -964
- package/dist/test/diff.examples.js.map +0 -1
- package/dist/test/ontology.examples.d.ts.map +0 -1
- package/dist/test/ontology.examples.js.map +0 -1
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/e3-ui/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
4
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
5
|
+
*/
|
|
6
|
+
/** @jsxImportSource @elaraai/e3-ui */
|
|
7
|
+
/**
|
|
8
|
+
* Diff component examples — realistic editing scenes that combine
|
|
9
|
+
* `Data.bind` with east-ui form components and a `<Diff>` panel
|
|
10
|
+
* surfacing the pending changes for sign-off.
|
|
11
|
+
*
|
|
12
|
+
* Coverage:
|
|
13
|
+
* 1. Slider-driven editor → workforcePolicyEditor
|
|
14
|
+
* 2. Heterogeneous inputs → serviceConfigForm
|
|
15
|
+
* 3. Editable Table cells → rosterTableEditor
|
|
16
|
+
* 4. Mixed sliders + inputs → pricingRulesEditor (side-by-side mode)
|
|
17
|
+
* 5. Minimal call site → diffDefaults (IR-roundtrip)
|
|
18
|
+
*
|
|
19
|
+
* Pattern:
|
|
20
|
+
* 1. Declare an `e3.input(name, type, default)` per editable scalar.
|
|
21
|
+
* 2. Inside `<Reactive>`, bind each via `Data.bind`.
|
|
22
|
+
* 3. Wire form components — `read()` for the current value,
|
|
23
|
+
* `write()` on change.
|
|
24
|
+
* 4. Close the scene with `<Diff bindings={[...]} />` — Apply commits
|
|
25
|
+
* the merged batch; Discard drops the buffer.
|
|
26
|
+
*/
|
|
27
|
+
import { East, FloatType, IntegerType, StringType, BooleanType, DateTimeType, ArrayType, StructType, SetType, DictType, VariantType, NullType, PatchType, diffFor, some, variant, example, } from "@elaraai/east";
|
|
28
|
+
import { Card, HStack, VStack, Slider, Input, Switch, Select, Text, Button, Reactive, Table, UIComponentType, } from "@elaraai/east-ui";
|
|
29
|
+
import { Data, Diff } from "@elaraai/e3-ui";
|
|
30
|
+
import * as e3 from "@elaraai/e3";
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Inputs — flat scalars for slider/input examples; one ArrayType binding for
|
|
33
|
+
// the table example so Diff can surface array-element patches end-to-end.
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Workforce-policy scenario (sliders)
|
|
36
|
+
export const maxWeeklyHoursInput = e3.input("max_weekly_hours", FloatType, 38.0);
|
|
37
|
+
export const overtimeThresholdInput = e3.input("overtime_threshold_hours", FloatType, 38.0);
|
|
38
|
+
export const restGapHoursInput = e3.input("mandatory_rest_gap_hours", FloatType, 12.0);
|
|
39
|
+
export const holidayPenaltyInput = e3.input("public_holiday_penalty", FloatType, 1.5);
|
|
40
|
+
// Service-config scenario (heterogeneous inputs)
|
|
41
|
+
export const serviceNameInput = e3.input("service_name", StringType, "auth-svc");
|
|
42
|
+
export const replicasInput = e3.input("replicas", IntegerType, 3n);
|
|
43
|
+
export const autoScaleInput = e3.input("auto_scale", BooleanType, false);
|
|
44
|
+
export const regionInput = e3.input("region", StringType, "ap-southeast-2");
|
|
45
|
+
export const deployAfterInput = e3.input("deploy_after", DateTimeType, new Date("2026-05-01T08:00:00Z"));
|
|
46
|
+
// Roster table scenario (single ArrayType<Struct> binding)
|
|
47
|
+
const RosterEntryType = StructType({
|
|
48
|
+
id: StringType,
|
|
49
|
+
name: StringType,
|
|
50
|
+
rate: FloatType,
|
|
51
|
+
shiftLength: IntegerType,
|
|
52
|
+
});
|
|
53
|
+
const RosterArrayType = ArrayType(RosterEntryType);
|
|
54
|
+
export const rosterInput = e3.input("roster", RosterArrayType, [
|
|
55
|
+
{ id: "alice", name: "Alice Chen", rate: 32.50, shiftLength: 8n },
|
|
56
|
+
{ id: "bob", name: "Bob Romero", rate: 28.00, shiftLength: 8n },
|
|
57
|
+
{ id: "charlie", name: "Charlie Patel", rate: 35.75, shiftLength: 10n },
|
|
58
|
+
{ id: "diana", name: "Diana Wallace", rate: 30.25, shiftLength: 8n },
|
|
59
|
+
]);
|
|
60
|
+
// Merge-conflict demo scenario — single Float; both bindStaged (for the user's
|
|
61
|
+
// pending edit) and bindDirect (for the "simulate concurrent edit" button)
|
|
62
|
+
// point at the same path so writes via the button drift the server view away
|
|
63
|
+
// from the staged buffer's pinned snapshot.
|
|
64
|
+
export const mergeDemoHoursInput = e3.input("merge_demo_hours", FloatType, 38.0);
|
|
65
|
+
// Pricing-rules scenario (mixed slider + integer/string input)
|
|
66
|
+
export const listPriceInput = e3.input("list_price", FloatType, 49.95);
|
|
67
|
+
export const discountPctInput = e3.input("discount_pct", FloatType, 10.0);
|
|
68
|
+
export const minOrderQtyInput = e3.input("min_order_qty", IntegerType, 1n);
|
|
69
|
+
export const currencyCodeInput = e3.input("currency_code", StringType, "AUD");
|
|
70
|
+
// Feature flags scenario (Set<String>)
|
|
71
|
+
export const featureFlagsInput = e3.input("feature_flags", SetType(StringType), new Set(["dark_mode", "experiments"]));
|
|
72
|
+
// Regional pricing scenario (Dict<String, Float>)
|
|
73
|
+
export const regionalPricesInput = e3.input("regional_prices", DictType(StringType, FloatType), new Map([
|
|
74
|
+
["AU", 49.95],
|
|
75
|
+
["US", 39.95],
|
|
76
|
+
["EU", 44.95],
|
|
77
|
+
["JP", 5499.0],
|
|
78
|
+
]));
|
|
79
|
+
// Deployment status scenario (Variant)
|
|
80
|
+
const DeploymentStatusType = VariantType({
|
|
81
|
+
pending: NullType,
|
|
82
|
+
in_progress: NullType,
|
|
83
|
+
complete: NullType,
|
|
84
|
+
failed: NullType,
|
|
85
|
+
});
|
|
86
|
+
export const deploymentStatusInput = e3.input("deployment_status", DeploymentStatusType, variant("pending", null));
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// 1. Workforce-policy editor — slider-driven edits across 4 staged scalars
|
|
89
|
+
// ============================================================================
|
|
90
|
+
export const workforcePolicyEditor = example({
|
|
91
|
+
keywords: ["Diff", "Slider", "bindStaged", "workforce", "policy", "Card"],
|
|
92
|
+
description: "Slider-driven workforce-policy editor — 4 staged Float scalars; Diff surfaces pending edits",
|
|
93
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
94
|
+
return (_jsx(Reactive, { children: $ => {
|
|
95
|
+
const maxHours = $.let(Data.bind([FloatType], maxWeeklyHoursInput.path));
|
|
96
|
+
const otThresh = $.let(Data.bind([FloatType], overtimeThresholdInput.path));
|
|
97
|
+
const restGap = $.let(Data.bind([FloatType], restGapHoursInput.path));
|
|
98
|
+
const penalty = $.let(Data.bind([FloatType], holidayPenaltyInput.path));
|
|
99
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Workforce policy" }), _jsx(Text, { children: "Drag the sliders to stage policy changes \u2014 review in the Diff card before applying." }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Max weekly hours" }), _jsx(Slider, { value: maxHours.read(), min: 30.0, max: 60.0, step: 1.0, onChangeEnd: ($, v) => $(maxHours.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Overtime threshold (h)" }), _jsx(Slider, { value: otThresh.read(), min: 30.0, max: 60.0, step: 1.0, onChangeEnd: ($, v) => $(otThresh.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Mandatory rest gap (h)" }), _jsx(Slider, { value: restGap.read(), min: 8.0, max: 16.0, step: 1.0, onChangeEnd: ($, v) => $(restGap.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Public holiday penalty (\u00D7)" }), _jsx(Slider, { value: penalty.read(), min: 1.0, max: 3.0, step: 0.25, onChangeEnd: ($, v) => $(penalty.write(v)) })] }), _jsx(Diff, { bindings: [maxHours.binding, otThresh.binding, restGap.binding, penalty.binding], hideUnchanged: some(true) })] }) }));
|
|
100
|
+
} }));
|
|
101
|
+
}),
|
|
102
|
+
inputs: [],
|
|
103
|
+
});
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// 2. Service-config form — heterogeneous Inputs (String / Integer / DateTime),
|
|
106
|
+
// Switch, Select; commit fires a Toast.
|
|
107
|
+
// ============================================================================
|
|
108
|
+
export const serviceConfigForm = example({
|
|
109
|
+
keywords: ["Diff", "Input", "Switch", "Select", "DateTime", "Toast", "onCommitted"],
|
|
110
|
+
description: "Service-config form covering String / Integer / Bool / DateTime / Select; commit fires a success Toast",
|
|
111
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
112
|
+
return (_jsx(Reactive, { children: $ => {
|
|
113
|
+
const svcName = $.let(Data.bind([StringType], serviceNameInput.path));
|
|
114
|
+
const replicas = $.let(Data.bind([IntegerType], replicasInput.path));
|
|
115
|
+
const autoScale = $.let(Data.bind([BooleanType], autoScaleInput.path));
|
|
116
|
+
const region = $.let(Data.bind([StringType], regionInput.path));
|
|
117
|
+
const deployAfter = $.let(Data.bind([DateTimeType], deployAfterInput.path));
|
|
118
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "4", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Service configuration" }), _jsx(Text, { children: "Stage edits across heterogeneous types; on apply you\u2019ll see a confirmation toast." }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Service name" }), _jsx(Input.String, { value: svcName.read(), placeholder: "service-name", onChange: ($, v) => $(svcName.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Replicas" }), _jsx(Input.Integer, { value: replicas.read(), min: 1n, max: 50n, onChange: ($, v) => $(replicas.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Auto-scale" }), _jsx(Switch, { checked: autoScale.read(), onChange: ($, v) => $(autoScale.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Region" }), _jsx(Select, { value: region.read(), items: [
|
|
119
|
+
Select.Item("ap-southeast-2", "ap-southeast-2 (Sydney)"),
|
|
120
|
+
Select.Item("us-east-1", "us-east-1 (N. Virginia)"),
|
|
121
|
+
Select.Item("eu-west-1", "eu-west-1 (Ireland)"),
|
|
122
|
+
], onChange: ($, v) => $(region.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Deploy after" }), _jsx(Input.DateTime, { value: deployAfter.read(), precision: "datetime", onChange: ($, v) => $(deployAfter.write(v)) })] }), _jsx(Diff, { bindings: [svcName.binding, replicas.binding, autoScale.binding, region.binding, deployAfter.binding] })] }) }));
|
|
123
|
+
} }));
|
|
124
|
+
}),
|
|
125
|
+
inputs: [],
|
|
126
|
+
});
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// 3. Roster Table — editable cells (Float / Integer Inputs) writing back to
|
|
129
|
+
// a single ArrayType<Struct> staged binding. Diff visualises array
|
|
130
|
+
// patches per row.
|
|
131
|
+
// ============================================================================
|
|
132
|
+
export const rosterTableEditor = example({
|
|
133
|
+
keywords: ["Diff", "Table", "cell", "render", "Input", "Array", "bindStaged", "roster"],
|
|
134
|
+
description: "Editable Table — Input.Float / Input.Integer cells write back to a staged roster array; Diff shows per-row updates",
|
|
135
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
136
|
+
return (_jsx(Reactive, { children: $ => {
|
|
137
|
+
const roster = $.let(Data.bind([RosterArrayType], rosterInput.path));
|
|
138
|
+
// Get the roster array fresh inside the render closures to ensure edits to sibling
|
|
139
|
+
const rosterArray = $.let(roster.read(), RosterArrayType);
|
|
140
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Worker roster" }), _jsx(Text, { children: "Edit hourly rates and default shift lengths inline; Diff card below tracks pending changes." }), _jsx(Table, { data: rosterArray, columns: {
|
|
141
|
+
name: { header: "Name" },
|
|
142
|
+
rate: {
|
|
143
|
+
header: "Hourly rate ($)",
|
|
144
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
145
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
146
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
147
|
+
return (_jsx(Input.Float, { value: row.rate, min: 15.0, max: 80.0, step: 0.25,
|
|
148
|
+
// Re-read fresh inside the handler so a sibling cell's
|
|
149
|
+
// edit on the same row isn't overwritten by a stale
|
|
150
|
+
// capture of `row`. Merge via the map iterator's `r`.
|
|
151
|
+
onChange: East.function([FloatType], NullType, ($, v) => {
|
|
152
|
+
const fresh = $.let(roster.read(), RosterArrayType);
|
|
153
|
+
const next = $.let(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, rate: v }), () => r)), RosterArrayType);
|
|
154
|
+
$(roster.write(next));
|
|
155
|
+
}) }));
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
shiftLength: {
|
|
159
|
+
header: "Shift length (h)",
|
|
160
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
161
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
162
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
163
|
+
return (_jsx(Input.Integer, { value: row.shiftLength, min: 4n, max: 12n, onChange: East.function([IntegerType], NullType, ($, v) => {
|
|
164
|
+
const fresh = $.let(roster.read(), RosterArrayType);
|
|
165
|
+
$(roster.write(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, shiftLength: v }), () => r))));
|
|
166
|
+
}) }));
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
}, variant: "line", striped: true }), _jsx(Diff, { bindings: [roster.binding], hideUnchanged: some(true) })] }) }));
|
|
170
|
+
} }));
|
|
171
|
+
}),
|
|
172
|
+
inputs: [],
|
|
173
|
+
});
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// 4. Pricing-rules editor — sliders + integer/string inputs together;
|
|
176
|
+
// side-by-side Diff mode for a wider review surface.
|
|
177
|
+
// ============================================================================
|
|
178
|
+
export const pricingRulesEditor = example({
|
|
179
|
+
keywords: ["Diff", "side-by-side", "Slider", "Input", "pricing", "discount", "mixed"],
|
|
180
|
+
description: "Pricing rules editor — sliders for list price/discount, IntegerInput for min order qty, StringInput for currency; Diff in side-by-side mode",
|
|
181
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
182
|
+
return (_jsx(Reactive, { children: $ => {
|
|
183
|
+
const listPrice = $.let(Data.bind([FloatType], listPriceInput.path));
|
|
184
|
+
const discountPct = $.let(Data.bind([FloatType], discountPctInput.path));
|
|
185
|
+
const minOrderQty = $.let(Data.bind([IntegerType], minOrderQtyInput.path));
|
|
186
|
+
const currency = $.let(Data.bind([StringType], currencyCodeInput.path));
|
|
187
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Pricing rules" }), _jsx(Text, { children: "Stage pricing changes \u2014 review side-by-side before applying to the catalog." }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "List price" }), _jsx(Slider, { value: listPrice.read(), min: 0.0, max: 999.95, step: 0.05, onChangeEnd: ($, v) => $(listPrice.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Discount (%)" }), _jsx(Slider, { value: discountPct.read(), min: 0.0, max: 75.0, step: 0.5, onChangeEnd: ($, v) => $(discountPct.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Minimum order quantity" }), _jsx(Input.Integer, { value: minOrderQty.read(), min: 1n, max: 1000n, onChange: ($, v) => $(minOrderQty.write(v)) })] }), _jsxs(VStack, { gap: "1", children: [_jsx(Text, { textStyle: "label-sm", children: "Currency code" }), _jsx(Input.String, { value: currency.read(), placeholder: "AUD", onChange: ($, v) => $(currency.write(v)) })] }), _jsx(Diff, { bindings: [listPrice.binding, discountPct.binding, minOrderQty.binding, currency.binding], hideUnchanged: some(true) })] }) }));
|
|
188
|
+
} }));
|
|
189
|
+
}),
|
|
190
|
+
inputs: [],
|
|
191
|
+
});
|
|
192
|
+
// ============================================================================
|
|
193
|
+
// 5. Feature flags editor — Set<String> binding; Switch per known flag.
|
|
194
|
+
// Demonstrates Set insert/delete patches in the Diff.
|
|
195
|
+
// ============================================================================
|
|
196
|
+
export const featureFlagsEditor = example({
|
|
197
|
+
keywords: ["Diff", "Set", "Switch", "feature flags", "bindStaged"],
|
|
198
|
+
description: "Feature-flags editor — Switch per known flag toggles inclusion in a staged Set; Diff shows insertions / deletions",
|
|
199
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
200
|
+
return (_jsx(Reactive, { children: $ => {
|
|
201
|
+
const flags = $.let(Data.bind([SetType(StringType)], featureFlagsInput.path));
|
|
202
|
+
const flagsRead = $.let(flags.read(), SetType(StringType));
|
|
203
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "4", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Feature flags" }), _jsx(Text, { children: "Toggle flags to stage changes; Diff card below shows the set delta." }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "dark_mode" }), _jsx(Switch, { checked: flagsRead.has("dark_mode"), onChange: East.function([BooleanType], NullType, ($, isOn) => {
|
|
204
|
+
const next = $.let(flags.read(), SetType(StringType));
|
|
205
|
+
$.if(isOn, $ => { $(next.insert("dark_mode")); }).else($ => { $(next.delete("dark_mode")); });
|
|
206
|
+
$(flags.write(next));
|
|
207
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "experiments" }), _jsx(Switch, { checked: flagsRead.has("experiments"), onChange: East.function([BooleanType], NullType, ($, isOn) => {
|
|
208
|
+
const next = $.let(flags.read(), SetType(StringType));
|
|
209
|
+
$.if(isOn, $ => { $(next.insert("experiments")); }).else($ => { $(next.delete("experiments")); });
|
|
210
|
+
$(flags.write(next));
|
|
211
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "notifications" }), _jsx(Switch, { checked: flagsRead.has("notifications"), onChange: East.function([BooleanType], NullType, ($, isOn) => {
|
|
212
|
+
const next = $.let(flags.read(), SetType(StringType));
|
|
213
|
+
$.if(isOn, $ => { $(next.insert("notifications")); }).else($ => { $(next.delete("notifications")); });
|
|
214
|
+
$(flags.write(next));
|
|
215
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "analytics" }), _jsx(Switch, { checked: flagsRead.has("analytics"), onChange: East.function([BooleanType], NullType, ($, isOn) => {
|
|
216
|
+
const next = $.let(flags.read(), SetType(StringType));
|
|
217
|
+
$.if(isOn, $ => { $(next.insert("analytics")); }).else($ => { $(next.delete("analytics")); });
|
|
218
|
+
$(flags.write(next));
|
|
219
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "ai_assist" }), _jsx(Switch, { checked: flagsRead.has("ai_assist"), onChange: East.function([BooleanType], NullType, ($, isOn) => {
|
|
220
|
+
const next = $.let(flags.read(), SetType(StringType));
|
|
221
|
+
$.if(isOn, $ => { $(next.insert("ai_assist")); }).else($ => { $(next.delete("ai_assist")); });
|
|
222
|
+
$(flags.write(next));
|
|
223
|
+
}) })] }), _jsx(Diff, { bindings: [flags.binding], hideUnchanged: some(true) })] }) }));
|
|
224
|
+
} }));
|
|
225
|
+
}),
|
|
226
|
+
inputs: [],
|
|
227
|
+
});
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// 6. Regional pricing editor — Dict<String, Float>; Input per known key.
|
|
230
|
+
// Demonstrates Dict update patches per key in the Diff.
|
|
231
|
+
// ============================================================================
|
|
232
|
+
export const regionalPricingEditor = example({
|
|
233
|
+
keywords: ["Diff", "Dict", "Input", "regional pricing", "bindStaged"],
|
|
234
|
+
description: "Regional-pricing editor — Input.Float per region writes back to a staged Dict; Diff shows per-key updates",
|
|
235
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
236
|
+
return (_jsx(Reactive, { children: $ => {
|
|
237
|
+
const prices = $.let(Data.bind([DictType(StringType, FloatType)], regionalPricesInput.path));
|
|
238
|
+
const pricesRead = $.let(prices.read(), DictType(StringType, FloatType));
|
|
239
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "4", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Regional pricing" }), _jsx(Text, { children: "Edit per-region prices; Diff card below tracks pending updates." }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "AU" }), _jsx(Input.Float, { value: pricesRead.get("AU", East.function([StringType], FloatType, _$ => 0.0)), min: 0.0, max: 99999.0, step: 0.05, onChange: East.function([FloatType], NullType, ($, newPrice) => {
|
|
240
|
+
const next = $.let(prices.read(), DictType(StringType, FloatType));
|
|
241
|
+
$(next.insertOrUpdate("AU", newPrice));
|
|
242
|
+
$(prices.write(next));
|
|
243
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "US" }), _jsx(Input.Float, { value: pricesRead.get("US", East.function([StringType], FloatType, _$ => 0.0)), min: 0.0, max: 99999.0, step: 0.05, onChange: East.function([FloatType], NullType, ($, newPrice) => {
|
|
244
|
+
const next = $.let(prices.read(), DictType(StringType, FloatType));
|
|
245
|
+
$(next.insertOrUpdate("US", newPrice));
|
|
246
|
+
$(prices.write(next));
|
|
247
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "EU" }), _jsx(Input.Float, { value: pricesRead.get("EU", East.function([StringType], FloatType, _$ => 0.0)), min: 0.0, max: 99999.0, step: 0.05, onChange: East.function([FloatType], NullType, ($, newPrice) => {
|
|
248
|
+
const next = $.let(prices.read(), DictType(StringType, FloatType));
|
|
249
|
+
$(next.insertOrUpdate("EU", newPrice));
|
|
250
|
+
$(prices.write(next));
|
|
251
|
+
}) })] }), _jsxs(VStack, { gap: "3", justify: "space-between", children: [_jsx(Text, { textStyle: "label-sm", children: "JP" }), _jsx(Input.Float, { value: pricesRead.get("JP", East.function([StringType], FloatType, _$ => 0.0)), min: 0.0, max: 99999.0, step: 0.05, onChange: East.function([FloatType], NullType, ($, newPrice) => {
|
|
252
|
+
const next = $.let(prices.read(), DictType(StringType, FloatType));
|
|
253
|
+
$(next.insertOrUpdate("JP", newPrice));
|
|
254
|
+
$(prices.write(next));
|
|
255
|
+
}) })] }), _jsx(Diff, { bindings: [prices.binding], hideUnchanged: some(true) })] }) }));
|
|
256
|
+
} }));
|
|
257
|
+
}),
|
|
258
|
+
inputs: [],
|
|
259
|
+
});
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// 7. Deployment status editor — Variant binding; one Button per status.
|
|
262
|
+
// Demonstrates Variant tag changes in the Diff.
|
|
263
|
+
// ============================================================================
|
|
264
|
+
export const deploymentStatusEditor = example({
|
|
265
|
+
keywords: ["Diff", "Variant", "Button", "deployment status", "bindStaged"],
|
|
266
|
+
description: "Deployment-status editor — buttons set a Variant binding; Diff shows the status tag change",
|
|
267
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
268
|
+
return (_jsx(Reactive, { children: $ => {
|
|
269
|
+
const status = $.let(Data.bind([DeploymentStatusType], deploymentStatusInput.path));
|
|
270
|
+
const statusRead = $.let(status.read(), DeploymentStatusType);
|
|
271
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "4", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Deployment status" }), _jsx(Text, { children: "Current:" }), _jsx(Text, { textStyle: "label-md", children: statusRead.getTag() }), _jsxs(HStack, { gap: "2", children: [_jsx(Button, { onClick: East.function([], NullType, $ => $(status.write(variant("pending", null)))), children: "Pending" }), _jsx(Button, { onClick: East.function([], NullType, $ => $(status.write(variant("in_progress", null)))), children: "In progress" }), _jsx(Button, { onClick: East.function([], NullType, $ => $(status.write(variant("complete", null)))), children: "Complete" }), _jsx(Button, { onClick: East.function([], NullType, $ => $(status.write(variant("failed", null)))), children: "Failed" })] }), _jsx(Diff, { bindings: [status.binding], hideUnchanged: some(true) })] }) }));
|
|
272
|
+
} }));
|
|
273
|
+
}),
|
|
274
|
+
inputs: [],
|
|
275
|
+
});
|
|
276
|
+
// ============================================================================
|
|
277
|
+
// 8. Minimal call site — defaults only (used by IR-roundtrip + smoke tests)
|
|
278
|
+
// ============================================================================
|
|
279
|
+
export const diffDefaults = example({
|
|
280
|
+
keywords: ["Diff", "Root", "defaults", "minimal"],
|
|
281
|
+
description: "Minimal Diff — single binding, all options omitted (defaults from IR)",
|
|
282
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
283
|
+
return (_jsx(Reactive, { children: $ => {
|
|
284
|
+
const view = $.let(Data.bind([FloatType], maxWeeklyHoursInput.path));
|
|
285
|
+
return _jsx(Diff, { bindings: [view.binding] });
|
|
286
|
+
} }));
|
|
287
|
+
}),
|
|
288
|
+
inputs: [],
|
|
289
|
+
});
|
|
290
|
+
// ============================================================================
|
|
291
|
+
// Merge-conflict demo — bindStaged for the user's edit + bindDirect on the
|
|
292
|
+
// SAME path for a "simulate concurrent edit" button. Clicking the button
|
|
293
|
+
// writes a different value through the live cache, so the staged buffer's
|
|
294
|
+
// pinned snapshot drifts away from the current server value. Hitting Apply
|
|
295
|
+
// on the Diff card then triggers the orange chooser flow.
|
|
296
|
+
// ============================================================================
|
|
297
|
+
/**
|
|
298
|
+
* Demonstrates the staged-mode merge tool / orange chooser. Workflow:
|
|
299
|
+
*
|
|
300
|
+
* 1. Drag the slider — your edit gets staged (snapshot pinned in StagedStore).
|
|
301
|
+
* 2. Click "Simulate concurrent edit" — `Data.bind.write(42.0)` writes 42 to
|
|
302
|
+
* the same path through the live cache, mimicking another session
|
|
303
|
+
* committing while you have edits open. Server view is now 42; your
|
|
304
|
+
* pinned snapshot is still 38; your buffer is whatever you dragged to.
|
|
305
|
+
* 3. Click Apply — `detectConflictsFor(userPatch, serverPatch)` finds both
|
|
306
|
+
* sides touching the root, switches the renderer to conflict mode, and
|
|
307
|
+
* shows the orange chooser row: keep yours (your dragged value), keep
|
|
308
|
+
* theirs (42), or manual (type a fresh number).
|
|
309
|
+
*/
|
|
310
|
+
export const mergeConflictDemo = example({
|
|
311
|
+
keywords: ["Diff", "merge", "conflict", "chooser", "bindStaged", "Slider"],
|
|
312
|
+
description: "Demo of the merge tool's orange chooser — drag, click Simulate, then Apply",
|
|
313
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
314
|
+
return (_jsx(Reactive, { children: $ => {
|
|
315
|
+
const staged = $.let(Data.bind([FloatType], mergeDemoHoursInput.path));
|
|
316
|
+
const direct = $.let(Data.bind([FloatType], mergeDemoHoursInput.path, { mode: "direct" }));
|
|
317
|
+
const stagedValue = $.let(staged.read(), FloatType);
|
|
318
|
+
const serverValue = $.let(direct.read(), FloatType);
|
|
319
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Merge-conflict demo" }), _jsx(Text, { children: "1. Drag the slider — your edit lands in the StagedStore (snapshot pinned). "
|
|
320
|
+
+ "2. Click \"Simulate concurrent edit\" — writes 42 through the live cache, "
|
|
321
|
+
+ "mimicking another session committing while you have edits open. "
|
|
322
|
+
+ "3. Click Apply on the Diff card — the merge tool detects drift and fires "
|
|
323
|
+
+ "the orange chooser row." }), _jsxs(VStack, { gap: "2", children: [_jsx(Text, { textStyle: "label-sm", children: "Server (live cache):" }), _jsx(Text, { children: East.str `${serverValue}` })] }), _jsxs(VStack, { gap: "2", children: [_jsx(Text, { textStyle: "label-sm", children: "Your staged value:" }), _jsx(Text, { children: East.str `${stagedValue}` })] }), _jsx(Slider, { value: stagedValue, min: 30.0, max: 60.0, step: 1.0, onChangeEnd: ($, v) => $(staged.write(v)) }), _jsxs(VStack, { gap: "2", children: [_jsx(Button, { onClick: $ => $(direct.write(42.0)), children: "Simulate concurrent edit (server \u2190 42)" }), _jsx(Button, { onClick: $ => $(direct.write(38.0)), children: "Reset server to 38" })] }), _jsx(Diff, { bindings: [staged.binding], hideUnchanged: some(true) })] }) }));
|
|
324
|
+
} }));
|
|
325
|
+
}),
|
|
326
|
+
inputs: [],
|
|
327
|
+
});
|
|
328
|
+
// ============================================================================
|
|
329
|
+
// Density variants — same pricing-rules content rendered at the two
|
|
330
|
+
// non-default density presets, so the showcase can compare side-by-side
|
|
331
|
+
// against the default (`pricingRulesEditor` above).
|
|
332
|
+
// ============================================================================
|
|
333
|
+
/**
|
|
334
|
+
* Pricing-rules editor at `compact` density — tighter row padding and smaller
|
|
335
|
+
* type scale than the default `comfortable`. Suited to data-dense pages where
|
|
336
|
+
* the Diff card is one of several panels competing for vertical space.
|
|
337
|
+
*/
|
|
338
|
+
export const pricingRulesEditorCompact = example({
|
|
339
|
+
keywords: ["Diff", "density", "compact", "Slider", "Input", "pricing"],
|
|
340
|
+
description: "Pricing-rules editor with `density: \"compact\"` — same content as pricingRulesEditor, denser rows",
|
|
341
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
342
|
+
return (_jsx(Reactive, { children: $ => {
|
|
343
|
+
const listPrice = $.let(Data.bind([FloatType], listPriceInput.path));
|
|
344
|
+
const discountPct = $.let(Data.bind([FloatType], discountPctInput.path));
|
|
345
|
+
const minOrderQty = $.let(Data.bind([IntegerType], minOrderQtyInput.path));
|
|
346
|
+
const currency = $.let(Data.bind([StringType], currencyCodeInput.path));
|
|
347
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Pricing rules \u2014 compact" }), _jsx(Slider, { value: listPrice.read(), min: 0.0, max: 999.95, step: 0.05, onChangeEnd: ($, v) => $(listPrice.write(v)) }), _jsx(Slider, { value: discountPct.read(), min: 0.0, max: 75.0, step: 0.5, onChangeEnd: ($, v) => $(discountPct.write(v)) }), _jsx(Input.Integer, { value: minOrderQty.read(), min: 1n, max: 1000n, onChange: ($, v) => $(minOrderQty.write(v)) }), _jsx(Input.String, { value: currency.read(), placeholder: "AUD", onChange: ($, v) => $(currency.write(v)) }), _jsx(Diff, { bindings: [listPrice.binding, discountPct.binding, minOrderQty.binding, currency.binding], density: "compact", hideUnchanged: some(true) })] }) }));
|
|
348
|
+
} }));
|
|
349
|
+
}),
|
|
350
|
+
inputs: [],
|
|
351
|
+
});
|
|
352
|
+
/**
|
|
353
|
+
* Pricing-rules editor at `condensed` density — mission-control sizing.
|
|
354
|
+
* Tightest row padding and smallest type scale; pairs with status dashboards
|
|
355
|
+
* and other read-heavy contexts where a diff list is reference data, not the
|
|
356
|
+
* main interaction surface.
|
|
357
|
+
*/
|
|
358
|
+
export const pricingRulesEditorCondensed = example({
|
|
359
|
+
keywords: ["Diff", "density", "condensed", "Slider", "Input", "pricing"],
|
|
360
|
+
description: "Pricing-rules editor with `density: \"condensed\"` — mission-control sizing for read-heavy views",
|
|
361
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
362
|
+
return (_jsx(Reactive, { children: $ => {
|
|
363
|
+
const listPrice = $.let(Data.bind([FloatType], listPriceInput.path));
|
|
364
|
+
const discountPct = $.let(Data.bind([FloatType], discountPctInput.path));
|
|
365
|
+
const minOrderQty = $.let(Data.bind([IntegerType], minOrderQtyInput.path));
|
|
366
|
+
const currency = $.let(Data.bind([StringType], currencyCodeInput.path));
|
|
367
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Pricing rules \u2014 condensed" }), _jsx(Slider, { value: listPrice.read(), min: 0.0, max: 999.95, step: 0.05, onChangeEnd: ($, v) => $(listPrice.write(v)) }), _jsx(Slider, { value: discountPct.read(), min: 0.0, max: 75.0, step: 0.5, onChangeEnd: ($, v) => $(discountPct.write(v)) }), _jsx(Input.Integer, { value: minOrderQty.read(), min: 1n, max: 1000n, onChange: ($, v) => $(minOrderQty.write(v)) }), _jsx(Input.String, { value: currency.read(), placeholder: "AUD", onChange: ($, v) => $(currency.write(v)) }), _jsx(Diff, { bindings: [listPrice.binding, discountPct.binding, minOrderQty.binding, currency.binding], density: "condensed", hideUnchanged: some(true) })] }) }));
|
|
368
|
+
} }));
|
|
369
|
+
}),
|
|
370
|
+
inputs: [],
|
|
371
|
+
});
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// Overlay-mode bindings — patches stored in a sibling `e3.input` typed
|
|
374
|
+
// `PatchType(T)`. The Diff component surfaces them via the `overlay` option
|
|
375
|
+
// instead of `staged`. See `Data.bindOverlay`.
|
|
376
|
+
// ============================================================================
|
|
377
|
+
// Patch inputs for overlay-mode examples. Each defaults to `unchanged`.
|
|
378
|
+
export const maxWeeklyHoursPatchInput = e3.input("max_weekly_hours_patch", PatchType(FloatType), variant("unchanged", null));
|
|
379
|
+
export const regionalPricesPatchInput = e3.input("regional_prices_patch", PatchType(DictType(StringType, FloatType)), variant("unchanged", null));
|
|
380
|
+
export const rosterPatchInput = e3.input("roster_patch", PatchType(RosterArrayType), variant("unchanged", null));
|
|
381
|
+
// Drift-laden patch input — defaults to a non-trivial patch whose ops are
|
|
382
|
+
// stale relative to the source dict `{AU: 49.95, US: 39.95, EU: 44.95, JP:
|
|
383
|
+
// 5499.0}`. Used by `regionalPricingOverlayDrift` to demonstrate what the
|
|
384
|
+
// Diff card surfaces when a server-stored patch was authored against an
|
|
385
|
+
// older version of its source. Conflicts (per `apply.ts` semantics):
|
|
386
|
+
// - `delete "MX"` — key not in source → stale delete
|
|
387
|
+
// - `insert "AU"` — key already exists in source → stale insert
|
|
388
|
+
// - `update "US"` with `replace(before=30 → 25)` — before mismatches source's 39.95
|
|
389
|
+
// - `update "EU"` with `replace(before=44.95 → 39.95)` — clean (for contrast)
|
|
390
|
+
export const regionalPricesDriftPatchInput = e3.input("regional_prices_drift_patch", PatchType(DictType(StringType, FloatType)), variant("patch", new Map([
|
|
391
|
+
["MX", variant("delete", 99.0)],
|
|
392
|
+
["AU", variant("insert", 100.0)],
|
|
393
|
+
["US", variant("update", variant("replace", { before: 30.0, after: 25.0 }))],
|
|
394
|
+
["EU", variant("update", variant("replace", { before: 44.95, after: 39.95 }))],
|
|
395
|
+
])));
|
|
396
|
+
// Roster overlay drift — a patch over the array-of-structs source. Built with
|
|
397
|
+
// `diffFor` (base matches the bound source, so ops are clean) so the Diff card
|
|
398
|
+
// exercises its nested grouping: binding → array index → struct field. Only
|
|
399
|
+
// the `rate` of entries 0 and 1 change, producing `[0] → rate`, `[1] → rate`.
|
|
400
|
+
const rosterDriftBase = [
|
|
401
|
+
{ id: "alice", name: "Alice Chen", rate: 32.50, shiftLength: 8n },
|
|
402
|
+
{ id: "bob", name: "Bob Romero", rate: 28.00, shiftLength: 8n },
|
|
403
|
+
{ id: "charlie", name: "Charlie Patel", rate: 35.75, shiftLength: 10n },
|
|
404
|
+
{ id: "diana", name: "Diana Wallace", rate: 30.25, shiftLength: 8n },
|
|
405
|
+
];
|
|
406
|
+
const rosterDriftEdited = [
|
|
407
|
+
{ id: "alice", name: "Alice Chen", rate: 6.00, shiftLength: 8n },
|
|
408
|
+
{ id: "bob", name: "Bob Romero", rate: 29.00, shiftLength: 8n },
|
|
409
|
+
{ id: "charlie", name: "Charlie Patel", rate: 35.75, shiftLength: 10n },
|
|
410
|
+
{ id: "diana", name: "Diana Wallace", rate: 30.25, shiftLength: 8n },
|
|
411
|
+
];
|
|
412
|
+
export const rosterDriftPatchInput = e3.input("roster_drift_patch", PatchType(RosterArrayType), diffFor(RosterArrayType)(rosterDriftBase, rosterDriftEdited));
|
|
413
|
+
/**
|
|
414
|
+
* Single-Float overlay editor — Slider edits go to `max_weekly_hours_patch`,
|
|
415
|
+
* the Diff card surfaces the patch against the source.
|
|
416
|
+
*/
|
|
417
|
+
export const policyOverlayEditor = example({
|
|
418
|
+
keywords: ["Diff", "bindOverlay", "Slider", "overlay", "policy", "patch"],
|
|
419
|
+
description: "Overlay-mode editor — Slider writes patches to a sibling e3.input; Diff shows them",
|
|
420
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
421
|
+
return (_jsx(Reactive, { children: $ => {
|
|
422
|
+
const view = $.let(Data.bind([FloatType], maxWeeklyHoursInput.path, { mode: "direct", patch: maxWeeklyHoursPatchInput.path }));
|
|
423
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Max weekly hours (overlay)" }), _jsx(Slider, { value: view.read(), min: 30.0, max: 60.0, step: 1.0, onChangeEnd: ($, v) => $(view.write(v)) }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
424
|
+
} }));
|
|
425
|
+
}),
|
|
426
|
+
inputs: [],
|
|
427
|
+
});
|
|
428
|
+
/**
|
|
429
|
+
* Overlay binding whose patch input *starts* with a non-trivial patch that
|
|
430
|
+
* is stale relative to the source. Demonstrates what the Diff card displays
|
|
431
|
+
* when a server-stored patch was authored against an older revision of its
|
|
432
|
+
* source — stale delete, stale insert, stale replace.
|
|
433
|
+
*
|
|
434
|
+
* No form components: `view.read()` calls `apply(source, patch)`, which
|
|
435
|
+
* throws `ConflictError` on the stale ops. The Diff card walks the patch IR
|
|
436
|
+
* directly from cache bytes (no apply) so it renders cleanly. The
|
|
437
|
+
* `bindOverlay` call is kept so the renderer's overlay-type registry knows
|
|
438
|
+
* how to decode the patch bytes for this binding.
|
|
439
|
+
*/
|
|
440
|
+
export const regionalPricingOverlayDrift = example({
|
|
441
|
+
keywords: ["Diff", "bindOverlay", "Dict", "conflict", "drift", "patch"],
|
|
442
|
+
description: "Overlay-mode example with a stale patch — Diff card surfaces ops that don't match the source",
|
|
443
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
444
|
+
return (_jsx(Reactive, { children: $ => {
|
|
445
|
+
const view = $.let(Data.bind([DictType(StringType, FloatType)], regionalPricesInput.path, { mode: "direct", patch: regionalPricesDriftPatchInput.path }));
|
|
446
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Regional pricing \u2014 overlay with stale patch" }), _jsx(Text, { children: "The patch input was authored against an older source. The Diff card below "
|
|
447
|
+
+ "shows: a stale delete (MX missing from source), a stale insert (AU already "
|
|
448
|
+
+ "exists), a stale replace (US before=30 doesn't match current 39.95), and "
|
|
449
|
+
+ "one clean replace (EU 44.95 → 39.95). Apply would throw ConflictError on "
|
|
450
|
+
+ "the stale ops; this example exists to show what the Diff renderer surfaces "
|
|
451
|
+
+ "when a server-stored patch has drifted from its source." }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
452
|
+
} }));
|
|
453
|
+
}),
|
|
454
|
+
inputs: [],
|
|
455
|
+
});
|
|
456
|
+
/**
|
|
457
|
+
* Roster overlay drift — array-of-structs patch surfaced by the Diff card,
|
|
458
|
+
* exercising nested grouping (binding → `[index]` → struct field). The patch
|
|
459
|
+
* touches only `rate` on the first two entries, so the card renders
|
|
460
|
+
* `ROSTER → [0] → rate`, `[1] → rate`.
|
|
461
|
+
*/
|
|
462
|
+
export const rosterOverlayDrift = example({
|
|
463
|
+
keywords: ["Diff", "overlay", "roster", "array", "nested", "patch"],
|
|
464
|
+
description: "Overlay-mode roster patch — Diff card surfaces nested array-element field changes",
|
|
465
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
466
|
+
return (_jsx(Reactive, { children: $ => {
|
|
467
|
+
const view = $.let(Data.bind([RosterArrayType], rosterInput.path, { mode: "direct", patch: rosterDriftPatchInput.path }));
|
|
468
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Roster \u2014 overlay patch (nested)" }), _jsx(Text, { children: "The patch changes the hourly rate of the first two roster entries. The "
|
|
469
|
+
+ "Diff card groups each change under its array index, demonstrating the "
|
|
470
|
+
+ "binding → [index] → field nesting." }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
471
|
+
} }));
|
|
472
|
+
}),
|
|
473
|
+
inputs: [],
|
|
474
|
+
});
|
|
475
|
+
/**
|
|
476
|
+
* Roster table editor in overlay mode — patches stored in `roster_patch`,
|
|
477
|
+
* survive page reload and are visible to other workspace sessions.
|
|
478
|
+
*/
|
|
479
|
+
export const rosterTableEditorOverlay = example({
|
|
480
|
+
keywords: ["Diff", "Table", "bindOverlay", "patch", "overlay", "roster"],
|
|
481
|
+
description: "Editable table backed by a separate patch e3.input — patches persist across sessions",
|
|
482
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
483
|
+
return (_jsx(Reactive, { children: $ => {
|
|
484
|
+
const view = $.let(Data.bind([RosterArrayType], rosterInput.path, { mode: "direct", patch: rosterPatchInput.path }));
|
|
485
|
+
const rosterArray = $.let(view.read(), RosterArrayType);
|
|
486
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Worker roster (overlay-mode)" }), _jsx(Text, { children: "Patches stored in a sibling dataset \u2014 survive reload, visible to other sessions." }), _jsx(Table, { data: rosterArray, columns: {
|
|
487
|
+
name: { header: "Name" },
|
|
488
|
+
rate: {
|
|
489
|
+
header: "Hourly rate ($)",
|
|
490
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
491
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
492
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
493
|
+
return (_jsx(Input.Float, { value: row.rate, min: 15.0, max: 80.0, step: 0.25, onChange: East.function([FloatType], NullType, ($, v) => {
|
|
494
|
+
const fresh = $.let(view.read(), RosterArrayType);
|
|
495
|
+
const next = $.let(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, rate: v }), _$ => r)), RosterArrayType);
|
|
496
|
+
$(view.write(next));
|
|
497
|
+
}) }));
|
|
498
|
+
}),
|
|
499
|
+
},
|
|
500
|
+
shiftLength: {
|
|
501
|
+
header: "Shift length (h)",
|
|
502
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
503
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
504
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
505
|
+
return (_jsx(Input.Integer, { value: row.shiftLength, min: 4n, max: 12n, onChange: East.function([IntegerType], NullType, ($, v) => {
|
|
506
|
+
const fresh = $.let(view.read(), RosterArrayType);
|
|
507
|
+
const next = $.let(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, shiftLength: v }), _$ => r)), RosterArrayType);
|
|
508
|
+
$(view.write(next));
|
|
509
|
+
}) }));
|
|
510
|
+
}),
|
|
511
|
+
},
|
|
512
|
+
}, variant: "line", striped: true }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
513
|
+
} }));
|
|
514
|
+
}),
|
|
515
|
+
inputs: [],
|
|
516
|
+
});
|
|
517
|
+
// ============================================================================
|
|
518
|
+
// Staged + patch dataset — the fourth mode. Edits buffer locally (IndexedDB)
|
|
519
|
+
// while the user iterates; commit publishes a fresh patch IR to the patch
|
|
520
|
+
// dataset (the source is not touched until a separate apply step). Pairs
|
|
521
|
+
// well with reviewer / approval workflows where the patch dataset becomes
|
|
522
|
+
// the unit of review.
|
|
523
|
+
// ============================================================================
|
|
524
|
+
/**
|
|
525
|
+
* Single-Float staged-with-patch editor — buffered Slider edits publish to
|
|
526
|
+
* `max_weekly_hours_patch` on Apply. The source `max_weekly_hours` stays
|
|
527
|
+
* untouched until a separate apply-patch-to-source step.
|
|
528
|
+
*/
|
|
529
|
+
export const policyStagedPatchEditor = example({
|
|
530
|
+
keywords: ["Diff", "Slider", "staged", "patch", "publish", "policy"],
|
|
531
|
+
description: "Slider edits buffered locally; Apply publishes draft to a server-backed patch dataset",
|
|
532
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
533
|
+
return (_jsx(Reactive, { children: $ => {
|
|
534
|
+
const view = $.let(Data.bind([FloatType], maxWeeklyHoursInput.path, { mode: "staged", patch: maxWeeklyHoursPatchInput.path }));
|
|
535
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Max weekly hours (staged + patch)" }), _jsx(Text, { children: "Drag to buffer locally; Apply publishes the draft as a patch to the patch dataset (source untouched)." }), _jsx(Slider, { value: view.read(), min: 30.0, max: 60.0, step: 1.0, onChange: ($, v) => $(view.write(v)) }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
536
|
+
} }));
|
|
537
|
+
}),
|
|
538
|
+
inputs: [],
|
|
539
|
+
});
|
|
540
|
+
/**
|
|
541
|
+
* Roster table editor in staged + patch mode. Cell edits buffer in
|
|
542
|
+
* IndexedDB until Apply, which publishes a fresh patch to `roster_patch`.
|
|
543
|
+
* Survives reload as a draft; multi-session reviewers see the patch only
|
|
544
|
+
* after the author commits.
|
|
545
|
+
*/
|
|
546
|
+
export const rosterStagedPatchEditor = example({
|
|
547
|
+
keywords: ["Diff", "Table", "staged", "patch", "publish", "roster"],
|
|
548
|
+
description: "Editable roster — edits buffered locally; Apply publishes a fresh patch to a server-backed patch dataset",
|
|
549
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
550
|
+
return (_jsx(Reactive, { children: $ => {
|
|
551
|
+
const view = $.let(Data.bind([RosterArrayType], rosterInput.path, { mode: "staged", patch: rosterPatchInput.path }));
|
|
552
|
+
const rosterArray = $.let(view.read(), RosterArrayType);
|
|
553
|
+
return (_jsx(Card, { children: _jsxs(VStack, { gap: "5", align: "stretch", children: [_jsx(Text, { textStyle: "heading-md", children: "Worker roster (staged + patch)" }), _jsx(Text, { children: "Edits buffer locally until Apply, which publishes a draft patch to the patch dataset for review." }), _jsx(Table, { data: rosterArray, columns: {
|
|
554
|
+
name: { header: "Name" },
|
|
555
|
+
rate: {
|
|
556
|
+
header: "Hourly rate ($)",
|
|
557
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
558
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
559
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
560
|
+
return (_jsx(Input.Float, { value: row.rate, min: 15.0, max: 80.0, step: 0.25, onChange: East.function([FloatType], NullType, ($, v) => {
|
|
561
|
+
const fresh = $.let(view.read(), RosterArrayType);
|
|
562
|
+
$(view.write(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, rate: v }), () => r))));
|
|
563
|
+
}) }));
|
|
564
|
+
}),
|
|
565
|
+
},
|
|
566
|
+
shiftLength: {
|
|
567
|
+
header: "Shift length (h)",
|
|
568
|
+
render: East.function([Table.Types.CellRenderContext], UIComponentType, ($, ctx) => {
|
|
569
|
+
const idx = $.let(ctx.rowIndex, IntegerType);
|
|
570
|
+
const row = $.let(rosterArray.get(idx), RosterEntryType);
|
|
571
|
+
return (_jsx(Input.Integer, { value: row.shiftLength, min: 4n, max: 12n, onChange: East.function([IntegerType], NullType, ($, v) => {
|
|
572
|
+
const fresh = $.let(view.read(), RosterArrayType);
|
|
573
|
+
$(view.write(fresh.map(($, r, i) => i.equal(idx).ifElse(_$ => ({ ...r, shiftLength: v }), () => r))));
|
|
574
|
+
}) }));
|
|
575
|
+
}),
|
|
576
|
+
},
|
|
577
|
+
}, variant: "line", striped: true }), _jsx(Diff, { bindings: [view.binding], hideUnchanged: some(true) })] }) }));
|
|
578
|
+
} }));
|
|
579
|
+
}),
|
|
580
|
+
inputs: [],
|
|
581
|
+
});
|
|
582
|
+
//# sourceMappingURL=diff.examples.js.map
|